Skip to content

Commit

Permalink
[expo-branch] First 3rd party library unimodule wrapper! (#5165)
Browse files Browse the repository at this point in the history
We would like to provide a way to users to optionally include `react-native-branch` in their standalone apps. Fixes #5132.

I added support for React Native-specific unimodules to React Native adapter. Right now it works like this:

- unimodule `expo-branch` depends on `react-native`, so it can use its API. It's no longer a uni-platform module, more an optinmodule (I guess the name won't stick)
- we copy `react-native-branch` vendored module directly to the unimodule. It will compile, since we added necessary dependencies to the unimodule
- `BranchPackage < org.unimodules.Package` implements `ReactPackage` interface too, forwarding all the calls to the vendored `RNBranchPackage`.
- when the module registry provider creates a module registry from a list of `Packages`, it can filter out `ReactPackages` — and so it does, putting them into a local internal module (`ReactPackagesProvider`) which only purpose is to carry those to `ModuleRegistryAdapter` which is responsible for creating a list of React Native modules out of a module registry. The adapter grabs the `ReactPackagesProvider` and adds React Native modules to the list.

I have confirmed that adding `BranchPackage` to a list of installed unimodules' packages creates `RNBranchModule` and exports to JS.
  • Loading branch information
sjchmiela committed Aug 6, 2019
1 parent a7218fe commit b600859
Show file tree
Hide file tree
Showing 100 changed files with 32,109 additions and 29,748 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Expand Up @@ -17,7 +17,7 @@ This is the log of notable changes to the Expo client that are developer-facing.
### 📚 3rd party library updates

- `react-native-gesture-handler` updated from `1.2.1` to `1.3.0`
- `react-native-branch` updated from `2.2.5` to `3.0.1`
- `react-native-branch` updated from `2.2.5` to `3.1.1`
- `react-native-reanimated` updated from `1.0.1` to `1.1.0`
- `react-native-svg` updated from `9.4.0` to `9.5.1`
- `react-native-webview` updated from `5.8.0` to `5.12.0`
Expand Down
Expand Up @@ -81,17 +81,6 @@ public List<NativeModule> createNativeModules(ScopedContext scopedContext, Exper
return getNativeModulesFromModuleRegistry(reactContext, moduleRegistry);
}

protected List<NativeModule> getNativeModulesFromModuleRegistry(ReactApplicationContext reactApplicationContext, ModuleRegistry moduleRegistry) {
List<NativeModule> nativeModulesList = new ArrayList<>(2);

nativeModulesList.add(new NativeModulesProxy(reactApplicationContext, moduleRegistry));

// Add listener that will notify org.unimodules.core.ModuleRegistry when all modules are ready
nativeModulesList.add(new ModuleRegistryReadyNotifier(moduleRegistry));

return nativeModulesList;
}

@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
throw new RuntimeException("Use createNativeModules(ReactApplicationContext, ExperienceId, JSONObject, List<NativeModule>) to get a list of native modules.");
Expand Down
100 changes: 12 additions & 88 deletions ios/Exponent.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

17 changes: 0 additions & 17 deletions ios/Exponent/ExpoKit/ExpoKit.m
Expand Up @@ -11,7 +11,6 @@
#import "EXKernelLinkingManager.h"
#import "EXReactAppExceptionHandler.h"
#import "EXRemoteNotificationManager.h"
#import "EXBranchManager.h"

#import <Crashlytics/Crashlytics.h>
#import <GoogleMaps/GoogleMaps.h>
Expand Down Expand Up @@ -109,7 +108,6 @@ - (void)application:(UIApplication *)application didFinishLaunchingWithOptions:(
// This is safe to call; if the app doesn't have permission to display user-facing notifications
// then registering for a push token is a no-op
[[EXKernel sharedInstance].serviceRegistry.remoteNotificationManager registerForRemoteNotifications];
[[EXKernel sharedInstance].serviceRegistry.branchManager application:application didFinishLaunchingWithOptions:launchOptions];
_launchOptions = launchOptions;
}

Expand Down Expand Up @@ -167,26 +165,11 @@ - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceAppl
return YES;
}

if ([[EXKernel sharedInstance].serviceRegistry.branchManager
application:application
openURL:url
sourceApplication:sourceApplication
annotation:annotation]) {
return YES;
}

return [EXKernelLinkingManager application:application openURL:url sourceApplication:sourceApplication annotation:annotation];
}

- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray * _Nullable))restorationHandler
{
if ([[EXKernel sharedInstance].serviceRegistry.branchManager
application:application
continueUserActivity:userActivity
restorationHandler:restorationHandler]) {
return YES;
}

if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
NSURL *webpageURL = userActivity.webpageURL;
if ([EXEnvironment sharedEnvironment].isDetached) {
Expand Down
2 changes: 0 additions & 2 deletions ios/Exponent/Kernel/Core/EXKernelServiceRegistry.h
Expand Up @@ -2,7 +2,6 @@

#import "EXKernelAppRegistry.h"

@class EXBranchManager;
@class EXErrorRecoveryManager;
@class EXFileSystemManager;
@class EXGoogleAuthManager;
Expand All @@ -16,7 +15,6 @@
@interface EXKernelServiceRegistry : NSObject <EXKernelAppRegistryDelegate>

// TODO: roll these into a macro in the respective classes instead of defining explicitly here.
@property (nonatomic, readonly) EXBranchManager *branchManager;
@property (nonatomic, readonly) EXErrorRecoveryManager *errorRecoveryManager;
@property (nonatomic, readonly) EXFileSystemManager *fileSystemManager;
@property (nonatomic, readonly) EXGoogleAuthManager *googleAuthManager;
Expand Down
12 changes: 0 additions & 12 deletions ios/Exponent/Kernel/Core/EXKernelServiceRegistry.m
@@ -1,7 +1,6 @@
// Copyright 2015-present 650 Industries. All rights reserved.

#import "EXKernelServiceRegistry.h"
#import "EXBranchManager.h"
#import "EXCachedResourceManager.h"
#import "EXErrorRecoveryManager.h"
#import "EXFileSystemManager.h"
Expand All @@ -21,7 +20,6 @@

@interface EXKernelServiceRegistry ()

@property (nonatomic, strong) EXBranchManager *branchManager;
@property (nonatomic, strong) EXCachedResourceManager *cachedResourceManager;
@property (nonatomic, strong) EXFileSystemManager *fileSystemManager;
@property (nonatomic, strong) EXGoogleAuthManager *googleAuthManager;
Expand All @@ -44,7 +42,6 @@ - (instancetype)init
{
if (self = [super init]) {
// TODO: init these in some clean way
[self branchManager];
[self cachedResourceManager];
[self errorRecoveryManager];
[self remoteNotificationManager];
Expand All @@ -61,14 +58,6 @@ - (instancetype)init
return self;
}

- (EXBranchManager *)branchManager
{
if (!_branchManager) {
_branchManager = [[EXBranchManager alloc] init];
}
return _branchManager;
}

- (EXCachedResourceManager *)cachedResourceManager
{
if (!_cachedResourceManager) {
Expand Down Expand Up @@ -176,7 +165,6 @@ - (NSDictionary *)allServices
// New singleton modules should register themselves in UMModuleRegistryProvider's set
// using EX_REGISTER_SINGLETON_MODULE macro.
NSArray *registryServices = @[
self.branchManager,
self.cachedResourceManager,
self.errorRecoveryManager,
self.fileSystemManager,
Expand Down
18 changes: 18 additions & 0 deletions ios/Exponent/Kernel/Services/EXScopedBranchManager.h
@@ -0,0 +1,18 @@
// Copyright 2015-present 650 Industries. All rights reserved.

#import <UIKit/UIKit.h>
#import <EXBranch/EXBranchManager.h>
#import "EXKernelService.h"

NS_ASSUME_NONNULL_BEGIN

/**
* Handles logic for Branch deep links and integration with the versioned
* RN bindings. Based loosely on RNBranch.h but handles versionning and limit
* usage to standalone apps.
*/
@interface EXScopedBranchManager : EXBranchManager <EXKernelService>

@end

NS_ASSUME_NONNULL_END
@@ -1,89 +1,62 @@
// Copyright 2015-present 650 Industries. All rights reserved.

#import "EXBranchManager.h"

#import <Branch/Branch.h>
#import "EXScopedBranchManager.h"
#import "EXScopedBranch.h"

#import "EXEnvironment.h"
#import "EXKernel.h"
#import "EXKernelAppRecord.h"
#import "RNBranch.h"
#import "EXScopedBranch.h"

// These constants need to stay in sync with the ones in RNBranch.
NSString * const EXBranchLinkOpenedNotificationErrorKey = @"error";
NSString * const EXBranchLinkOpenedNotificationParamsKey = @"params";
NSString * const EXBranchLinkOpenedNotificationUriKey = @"uri";
NSString * const EXBranchLinkOpenedNotification = @"RNBranchLinkOpenedNotification";

@interface RNBranchModuleAvoidWarnings
@interface EXScopedBranchModuleAvoidWarnings
- (void)onInitSessionFinished:(NSNotification *)notification;
@end

@interface EXBranchManager () <EXBranchScopedModuleDelegate>

@end

@implementation EXBranchManager
@implementation EXScopedBranchManager
{
NSDictionary *_launchOptions;
BOOL _isInitialized;
NSURL *_url;
}

+ (instancetype)sharedInstance
{
static EXBranchManager *theManager;
static dispatch_once_t once;
dispatch_once(&once, ^{
if (!theManager) {
theManager = [EXBranchManager new];
}
});
return theManager;
}

+ (BOOL)isBranchEnabled
{
id branchKey = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"branch_key"];
return (branchKey != nil);
}

#pragma mark - linking hooks
UM_REGISTER_SINGLETON_MODULE(BranchManager);

- (void)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
_launchOptions = launchOptions;
_url = launchOptions[UIApplicationLaunchOptionsURLKey];
[super application:application didFinishLaunchingWithOptions:launchOptions];
}

- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler
{
if (![[self class] isBranchEnabled]) {
return NO;
}
_url = userActivity.webpageURL;
return [[Branch getInstance] continueUserActivity:userActivity];
return [super application:application continueUserActivity:userActivity restorationHandler:restorationHandler];
}

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(nullable NSString *)sourceApplication annotation:(id)annotation
{
if (![[self class] isBranchEnabled]) {
return NO;
}
_url = url;
return [[Branch getInstance] handleDeepLink:url];
return [super application:application openURL:url sourceApplication:sourceApplication annotation:annotation];
}

- (void)branchModuleDidInit:(id)versionedBranchModule
{
if (_isInitialized || ![[self class] isBranchEnabled]) {
return;
}
RNBranch *branchModule = (RNBranch *)versionedBranchModule;

EXScopedBranch *branchModule = (EXScopedBranch *)versionedBranchModule;
EXKernelAppRecord *appForModule = [[EXKernel sharedInstance].appRegistry newestRecordWithExperienceId:branchModule.experienceId];
if (appForModule && appForModule == [EXKernel sharedInstance].appRegistry.standaloneAppRecord) {
_isInitialized = YES;

// branch is going to retain the init callback
__block typeof(self) blockSelf = self;

Expand Down
25 changes: 25 additions & 0 deletions ios/Exponent/Versioned/Core/UniversalModules/EXScopedBranch.h
@@ -0,0 +1,25 @@
// Copyright 2019-present 650 Industries. All rights reserved.

#if __has_include(<EXBranch/RNBranch.h>)
#import <EXBranch/RNBranch.h>
#import <UMCore/UMInternalModule.h>
#import <UMCore/UMModuleRegistryConsumer.h>

NS_ASSUME_NONNULL_BEGIN

@protocol EXBranchScopedModuleDelegate

- (void)branchModuleDidInit:(id _Nonnull)branchModule;

@end

@interface EXScopedBranch : RNBranch <UMModuleRegistryConsumer, UMInternalModule>

@property (nonatomic, strong) NSString *experienceId;

- (instancetype)initWithExperienceId:(NSString *)experienceId;

@end

NS_ASSUME_NONNULL_END
#endif
48 changes: 48 additions & 0 deletions ios/Exponent/Versioned/Core/UniversalModules/EXScopedBranch.m
@@ -0,0 +1,48 @@
// Copyright 2019-present 650 Industries. All rights reserved.
#if __has_include(<EXBranch/RNBranch.h>)
#import "EXScopedBranch.h"

@interface EXScopedBranch ()

@property (nonatomic, weak) UMModuleRegistry *moduleRegistry;

@end

@protocol EXDummyBranchProtocol
@end

@implementation EXScopedBranch

@synthesize bridge = _bridge;

+ (const NSArray<Protocol *> *)exportedInterfaces
{
return @[@protocol(EXDummyBranchProtocol)];
}

- (instancetype)initWithExperienceId:(NSString *)experienceId
{
if (self = [super init]) {
[[NSNotificationCenter defaultCenter] removeObserver:self name:RNBranchLinkOpenedNotification object:nil];
_experienceId = experienceId;
}
return self;
}

- (void)setModuleRegistry:(UMModuleRegistry *)moduleRegistry
{
_moduleRegistry = moduleRegistry;
[(id<EXBranchScopedModuleDelegate>)[_moduleRegistry getSingletonModuleForName:@"BranchManager"] branchModuleDidInit:self];
}

- (void)setBridge:(RCTBridge *)bridge
{
_bridge = bridge;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onInitSessionFinished:) name:RNBranchLinkOpenedNotification object:nil];
#pragma clang diagnostic pop
}

@end
#endif
Expand Up @@ -14,6 +14,7 @@
#import "EXScopedPermissions.h"
#import "EXScopedSegment.h"
#import "EXScopedLocalAuthentication.h"
#import "EXScopedBranch.h"

#import "EXScopedReactNativeAdapter.h"
#import "EXModuleRegistryBinding.h"
Expand Down Expand Up @@ -82,6 +83,11 @@ - (UMModuleRegistry *)moduleRegistryForParams:(NSDictionary *)params forExperien
[moduleRegistry registerExportedModule:segmentModule];
#endif

#if __has_include(<EXBranch/RNBranch.h>)
EXScopedBranch *branchModule = [[EXScopedBranch alloc] initWithExperienceId:experienceId];
[moduleRegistry registerInternalModule:branchModule];
#endif

#if __has_include(<EXLocalAuthentication/EXLocalAuthentication.h>)
EXScopedLocalAuthentication *localAuthenticationModule = [[EXScopedLocalAuthentication alloc] init];
[moduleRegistry registerExportedModule:localAuthenticationModule];
Expand Down

0 comments on commit b600859

Please sign in to comment.