From 5018b583d4f01a1115d4ee81641d8c0b21603c67 Mon Sep 17 00:00:00 2001 From: Maxime Crampon Date: Tue, 18 Jan 2022 18:57:36 +0100 Subject: [PATCH 1/7] feat(plugin): add ios expo plugin --- ...withReactNativeBatchAppBuildGradle.test.ts | 3 +- .../withReactNativeBatchAppDelegate.test.ts | 13 + plugin/src/fixtures/appDelegate.ts | 319 ++++++++++++++++++ plugin/src/fixtures/buildGradle.ts | 2 +- plugin/src/withReactNativeBatch.ts | 8 +- .../src/withReactNativeBatchAppBuildGradle.ts | 2 +- plugin/src/withReactNativeBatchAppDelegate.ts | 35 ++ plugin/src/withReactNativeBatchInfoPlist.ts | 20 ++ 8 files changed, 397 insertions(+), 5 deletions(-) create mode 100644 plugin/src/__tests__/withReactNativeBatchAppDelegate.test.ts create mode 100644 plugin/src/fixtures/appDelegate.ts create mode 100644 plugin/src/withReactNativeBatchAppDelegate.ts create mode 100644 plugin/src/withReactNativeBatchInfoPlist.ts diff --git a/plugin/src/__tests__/withReactNativeBatchAppBuildGradle.test.ts b/plugin/src/__tests__/withReactNativeBatchAppBuildGradle.test.ts index 8b3ff0e..cb38649 100644 --- a/plugin/src/__tests__/withReactNativeBatchAppBuildGradle.test.ts +++ b/plugin/src/__tests__/withReactNativeBatchAppBuildGradle.test.ts @@ -7,7 +7,8 @@ import { describe(pushDependencies, () => { it('should push depedencies in the App ProjetGradle file', () => { const result = pushDependencies(buildGradleFixture, { - apiKey: 'FAKE_API_KEY', + iOSApiKey: 'FAKE_IOS_API_KEY', + androidApiKey: 'FAKE_ANDROID_API_KEY', }); expect(result).toEqual(buildGradleExpectedFixture); diff --git a/plugin/src/__tests__/withReactNativeBatchAppDelegate.test.ts b/plugin/src/__tests__/withReactNativeBatchAppDelegate.test.ts new file mode 100644 index 0000000..e4ba05e --- /dev/null +++ b/plugin/src/__tests__/withReactNativeBatchAppDelegate.test.ts @@ -0,0 +1,13 @@ +import { modifyAppDelegate } from '../withReactNativeBatchAppDelegate'; +import { + appDelegateExpectedFixture, + appDelegateFixture, +} from '../fixtures/appDelegate'; + +describe(modifyAppDelegate, () => { + it('should modify the AppDelegate', () => { + const result = modifyAppDelegate(appDelegateFixture); + + expect(result).toEqual(appDelegateExpectedFixture); + }); +}); diff --git a/plugin/src/fixtures/appDelegate.ts b/plugin/src/fixtures/appDelegate.ts new file mode 100644 index 0000000..ede54b3 --- /dev/null +++ b/plugin/src/fixtures/appDelegate.ts @@ -0,0 +1,319 @@ +export const appDelegateFixture = `#import "AppDelegate.h" + +#if defined(EX_DEV_MENU_ENABLED) +@import EXDevMenu; +#endif + +#if defined(EX_DEV_LAUNCHER_ENABLED) +#include +#import +#endif + +#import +#import +#import +#import + +#import +#import +#import +#import +#import + +#if defined(FB_SONARKIT_ENABLED) && __has_include() +#import +#import +#import +#import +#import +#import + +static void InitializeFlipper(UIApplication *application) { + FlipperClient *client = [FlipperClient sharedClient]; + SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults]; + [client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]]; + [client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]]; + [client addPlugin:[FlipperKitReactPlugin new]]; + [client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]]; + [client start]; +} +#endif + +@interface AppDelegate () + +@property (nonatomic, strong) UMModuleRegistryAdapter *moduleRegistryAdapter; +@property (nonatomic, strong) NSDictionary *launchOptions; + +@end + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ +#if defined(FB_SONARKIT_ENABLED) && __has_include() + InitializeFlipper(application); +#endif + + self.moduleRegistryAdapter = [[UMModuleRegistryAdapter alloc] initWithModuleRegistryProvider:[[UMModuleRegistryProvider alloc] init]]; + self.launchOptions = launchOptions; + self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; + #ifdef DEBUG + #if defined(EX_DEV_LAUNCHER_ENABLED) + EXDevLauncherController *controller = [EXDevLauncherController sharedInstance]; + controller.updatesInterface = [EXUpdatesDevLauncherController sharedInstance]; + [controller startWithWindow:self.window delegate:(id)self launchOptions:launchOptions]; + #else + [self initializeReactNativeApp]; + #endif + #else + EXUpdatesAppController *controller = [EXUpdatesAppController sharedInstance]; + controller.delegate = self; + [controller startAndShowLaunchScreen:self.window]; + #endif + + [super application:application didFinishLaunchingWithOptions:launchOptions]; + + return YES; +} + +- (RCTBridge *)initializeReactNativeApp +{ + #if defined(EX_DEV_LAUNCHER_ENABLED) + NSDictionary *launchOptions = [EXDevLauncherController.sharedInstance getLaunchOptions]; + #else + NSDictionary *launchOptions = self.launchOptions; + #endif + + RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; + RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"main" initialProperties:nil]; + rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; + + UIViewController *rootViewController = [UIViewController new]; + rootViewController.view = rootView; + self.window.rootViewController = rootViewController; + [self.window makeKeyAndVisible]; + + return bridge; + } + +- (NSArray> *)extraModulesForBridge:(RCTBridge *)bridge +{ + NSArray> *extraModules = [_moduleRegistryAdapter extraModulesForBridge:bridge]; + // If you'd like to export some custom RCTBridgeModules that are not Expo modules, add them here! + return extraModules; +} + +- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge { + #ifdef DEBUG + #if defined(EX_DEV_LAUNCHER_ENABLED) + return [[EXDevLauncherController sharedInstance] sourceUrl]; + #else + return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; + #endif + #else + return [[EXUpdatesAppController sharedInstance] launchAssetUrl]; + #endif +} + +- (void)appController:(EXUpdatesAppController *)appController didStartWithSuccess:(BOOL)success { + appController.bridge = [self initializeReactNativeApp]; + EXSplashScreenService *splashScreenService = (EXSplashScreenService *)[UMModuleRegistryProvider getSingletonModuleForClass:[EXSplashScreenService class]]; + [splashScreenService showSplashScreenFor:self.window.rootViewController]; +} + +// Linking API +- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options { + #if defined(EX_DEV_LAUNCHER_ENABLED) + if ([EXDevLauncherController.sharedInstance onDeepLink:url options:options]) { + return true; + } + #endif + return [RCTLinkingManager application:application openURL:url options:options]; +} + +// Universal Links +- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray> * _Nullable))restorationHandler { + return [RCTLinkingManager application:application + continueUserActivity:userActivity + restorationHandler:restorationHandler]; +} + +@end + +#if defined(EX_DEV_LAUNCHER_ENABLED) +@implementation AppDelegate (EXDevLauncherControllerDelegate) + +- (void)devLauncherController:(EXDevLauncherController *)developmentClientController + didStartWithSuccess:(BOOL)success +{ + developmentClientController.appBridge = [self initializeReactNativeApp]; + EXSplashScreenService *splashScreenService = (EXSplashScreenService *)[UMModuleRegistryProvider getSingletonModuleForClass:[EXSplashScreenService class]]; + [splashScreenService showSplashScreenFor:self.window.rootViewController]; +} + +@end +#endif +`; + +export const appDelegateExpectedFixture = `#import "AppDelegate.h" + +#if defined(EX_DEV_MENU_ENABLED) +@import EXDevMenu; +#endif + +#if defined(EX_DEV_LAUNCHER_ENABLED) +#include +#import +#endif + +#import +#import +#import +#import + +#import +#import +#import +#import +#import + +#if defined(FB_SONARKIT_ENABLED) && __has_include() +#import +#import +#import +#import +#import +#import + +static void InitializeFlipper(UIApplication *application) { + FlipperClient *client = [FlipperClient sharedClient]; + SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults]; + [client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]]; + [client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]]; + [client addPlugin:[FlipperKitReactPlugin new]]; + [client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]]; + [client start]; +} +#endif + +#import + +@interface AppDelegate () + +@property (nonatomic, strong) UMModuleRegistryAdapter *moduleRegistryAdapter; +@property (nonatomic, strong) NSDictionary *launchOptions; + +@end + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ +[RNBatch start]; +[BatchUNUserNotificationCenterDelegate registerAsDelegate]; +[BatchUNUserNotificationCenterDelegate sharedInstance].showForegroundNotifications = true; + +#if defined(FB_SONARKIT_ENABLED) && __has_include() + InitializeFlipper(application); +#endif + + self.moduleRegistryAdapter = [[UMModuleRegistryAdapter alloc] initWithModuleRegistryProvider:[[UMModuleRegistryProvider alloc] init]]; + self.launchOptions = launchOptions; + self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; + #ifdef DEBUG + #if defined(EX_DEV_LAUNCHER_ENABLED) + EXDevLauncherController *controller = [EXDevLauncherController sharedInstance]; + controller.updatesInterface = [EXUpdatesDevLauncherController sharedInstance]; + [controller startWithWindow:self.window delegate:(id)self launchOptions:launchOptions]; + #else + [self initializeReactNativeApp]; + #endif + #else + EXUpdatesAppController *controller = [EXUpdatesAppController sharedInstance]; + controller.delegate = self; + [controller startAndShowLaunchScreen:self.window]; + #endif + + [super application:application didFinishLaunchingWithOptions:launchOptions]; + + return YES; +} + +- (RCTBridge *)initializeReactNativeApp +{ + #if defined(EX_DEV_LAUNCHER_ENABLED) + NSDictionary *launchOptions = [EXDevLauncherController.sharedInstance getLaunchOptions]; + #else + NSDictionary *launchOptions = self.launchOptions; + #endif + + RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; + RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"main" initialProperties:nil]; + rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; + + UIViewController *rootViewController = [UIViewController new]; + rootViewController.view = rootView; + self.window.rootViewController = rootViewController; + [self.window makeKeyAndVisible]; + + return bridge; + } + +- (NSArray> *)extraModulesForBridge:(RCTBridge *)bridge +{ + NSArray> *extraModules = [_moduleRegistryAdapter extraModulesForBridge:bridge]; + // If you'd like to export some custom RCTBridgeModules that are not Expo modules, add them here! + return extraModules; +} + +- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge { + #ifdef DEBUG + #if defined(EX_DEV_LAUNCHER_ENABLED) + return [[EXDevLauncherController sharedInstance] sourceUrl]; + #else + return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; + #endif + #else + return [[EXUpdatesAppController sharedInstance] launchAssetUrl]; + #endif +} + +- (void)appController:(EXUpdatesAppController *)appController didStartWithSuccess:(BOOL)success { + appController.bridge = [self initializeReactNativeApp]; + EXSplashScreenService *splashScreenService = (EXSplashScreenService *)[UMModuleRegistryProvider getSingletonModuleForClass:[EXSplashScreenService class]]; + [splashScreenService showSplashScreenFor:self.window.rootViewController]; +} + +// Linking API +- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options { + #if defined(EX_DEV_LAUNCHER_ENABLED) + if ([EXDevLauncherController.sharedInstance onDeepLink:url options:options]) { + return true; + } + #endif + return [RCTLinkingManager application:application openURL:url options:options]; +} + +// Universal Links +- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray> * _Nullable))restorationHandler { + return [RCTLinkingManager application:application + continueUserActivity:userActivity + restorationHandler:restorationHandler]; +} + +@end + +#if defined(EX_DEV_LAUNCHER_ENABLED) +@implementation AppDelegate (EXDevLauncherControllerDelegate) + +- (void)devLauncherController:(EXDevLauncherController *)developmentClientController + didStartWithSuccess:(BOOL)success +{ + developmentClientController.appBridge = [self initializeReactNativeApp]; + EXSplashScreenService *splashScreenService = (EXSplashScreenService *)[UMModuleRegistryProvider getSingletonModuleForClass:[EXSplashScreenService class]]; + [splashScreenService showSplashScreenFor:self.window.rootViewController]; +} + +@end +#endif +`; diff --git a/plugin/src/fixtures/buildGradle.ts b/plugin/src/fixtures/buildGradle.ts index e8ad310..0b662c2 100644 --- a/plugin/src/fixtures/buildGradle.ts +++ b/plugin/src/fixtures/buildGradle.ts @@ -154,7 +154,7 @@ android { targetSdkVersion rootProject.ext.targetSdkVersion versionCode 1 versionName "1.0" - resValue "string", "BATCH_API_KEY", "FAKE_API_KEY" + resValue "string", "BATCH_API_KEY", "FAKE_ANDROID_API_KEY" } splits { abi { diff --git a/plugin/src/withReactNativeBatch.ts b/plugin/src/withReactNativeBatch.ts index 770910a..99b6762 100644 --- a/plugin/src/withReactNativeBatch.ts +++ b/plugin/src/withReactNativeBatch.ts @@ -7,13 +7,15 @@ import { import { withReactNativeBatchMainActivity } from './withReactNativeBatchMainActivity'; import { withReactNativeBatchAppBuildGradle } from './withReactNativeBatchAppBuildGradle'; import { withReactNativeBatchProjectBuildGradle } from './withReactNativeBatchProjectBuildGradle'; +import { withReactNativeBatchInfoPlist } from './withReactNativeBatchInfoPlist'; +import { withReactNativeBatchAppDelegate } from './withReactNativeBatchAppDelegate'; -export type Props = { apiKey: string }; +export type Props = { androidApiKey: string; iOSApiKey: string }; /** * Apply react-native-batch configuration for Expo SDK 42 projects. */ const withReactNativeBatch: ConfigPlugin = (config, props) => { - const _props = props || { apiKey: '' }; + const _props = props || { androidApiKey: '', iOSApiKey: '' }; let newConfig = withGoogleServicesFile(config); newConfig = withClassPath(newConfig); @@ -21,6 +23,8 @@ const withReactNativeBatch: ConfigPlugin = (config, props) => { newConfig = withReactNativeBatchAppBuildGradle(newConfig, _props); newConfig = withReactNativeBatchMainActivity(newConfig); newConfig = withReactNativeBatchProjectBuildGradle(newConfig); + newConfig = withReactNativeBatchInfoPlist(newConfig, _props); + newConfig = withReactNativeBatchAppDelegate(newConfig); // Return the modified config. return newConfig; }; diff --git a/plugin/src/withReactNativeBatchAppBuildGradle.ts b/plugin/src/withReactNativeBatchAppBuildGradle.ts index c34195c..73d45a2 100644 --- a/plugin/src/withReactNativeBatchAppBuildGradle.ts +++ b/plugin/src/withReactNativeBatchAppBuildGradle.ts @@ -20,7 +20,7 @@ export const pushDependencies = (contents: string, props: Props): string => { newContents = start + defaultConfigContents[0] + - ` resValue "string", "BATCH_API_KEY", "${props.apiKey}"` + + ` resValue "string", "BATCH_API_KEY", "${props.androidApiKey}"` + '\n ' + end; } diff --git a/plugin/src/withReactNativeBatchAppDelegate.ts b/plugin/src/withReactNativeBatchAppDelegate.ts new file mode 100644 index 0000000..a531f48 --- /dev/null +++ b/plugin/src/withReactNativeBatchAppDelegate.ts @@ -0,0 +1,35 @@ +import { ConfigPlugin, withAppDelegate } from '@expo/config-plugins'; +import { Props } from './withReactNativeBatch'; + +const END_OF_HEADER = '@interface AppDelegate () '; +const DID_FINISH_LAUNCHING_WITH_OPTIONS_DECLARATION = + '- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions\n{'; + +const IMPORT_BATCH = '#import \n\n'; +const REGISTER_BATCH = + '\n[RNBatch start];\n[BatchUNUserNotificationCenterDelegate registerAsDelegate];\n[BatchUNUserNotificationCenterDelegate sharedInstance].showForegroundNotifications = true;\n'; + +export const modifyAppDelegate = (contents: string) => { + const [header, __rest] = contents.split(END_OF_HEADER); + const newHeader = header.concat(IMPORT_BATCH).concat(END_OF_HEADER); + + contents = newHeader.concat(__rest); + + const [beforeDeclaration, afterDeclaration] = contents.split( + DID_FINISH_LAUNCHING_WITH_OPTIONS_DECLARATION + ); + + const newAfterDeclaration = DID_FINISH_LAUNCHING_WITH_OPTIONS_DECLARATION.concat( + REGISTER_BATCH + ).concat(afterDeclaration); + + contents = beforeDeclaration.concat(newAfterDeclaration); + return contents; +}; + +export const withReactNativeBatchAppDelegate: ConfigPlugin<{} | void> = config => { + return withAppDelegate(config, config => { + config.modResults.contents = modifyAppDelegate(config.modResults.contents); + return config; + }); +}; diff --git a/plugin/src/withReactNativeBatchInfoPlist.ts b/plugin/src/withReactNativeBatchInfoPlist.ts new file mode 100644 index 0000000..bc84861 --- /dev/null +++ b/plugin/src/withReactNativeBatchInfoPlist.ts @@ -0,0 +1,20 @@ +import { ConfigPlugin, withInfoPlist, InfoPlist } from '@expo/config-plugins'; +import { Props } from './withReactNativeBatch'; + +export const modifyInfoPlist = ( + infoPlist: InfoPlist, + props: Props +): InfoPlist => { + infoPlist.BatchAPIKey = props.iOSApiKey; + return infoPlist; +}; + +export const withReactNativeBatchInfoPlist: ConfigPlugin = ( + config, + props +) => { + return withInfoPlist(config, config => { + config.modResults = modifyInfoPlist(config.modResults, props); + return config; + }); +}; From 33b100a38e9d3175d7a0c0b92fe69618583561c7 Mon Sep 17 00:00:00 2001 From: Maxime Crampon Date: Thu, 20 Jan 2022 09:55:01 +0100 Subject: [PATCH 2/7] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20(expo)=20rename=20iOSA?= =?UTF-8?q?piKey=20to=20iosApiKey?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/__tests__/withReactNativeBatchAppBuildGradle.test.ts | 2 +- plugin/src/withReactNativeBatch.ts | 4 ++-- plugin/src/withReactNativeBatchInfoPlist.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugin/src/__tests__/withReactNativeBatchAppBuildGradle.test.ts b/plugin/src/__tests__/withReactNativeBatchAppBuildGradle.test.ts index cb38649..2e64116 100644 --- a/plugin/src/__tests__/withReactNativeBatchAppBuildGradle.test.ts +++ b/plugin/src/__tests__/withReactNativeBatchAppBuildGradle.test.ts @@ -7,7 +7,7 @@ import { describe(pushDependencies, () => { it('should push depedencies in the App ProjetGradle file', () => { const result = pushDependencies(buildGradleFixture, { - iOSApiKey: 'FAKE_IOS_API_KEY', + iosApiKey: 'FAKE_IOS_API_KEY', androidApiKey: 'FAKE_ANDROID_API_KEY', }); diff --git a/plugin/src/withReactNativeBatch.ts b/plugin/src/withReactNativeBatch.ts index 99b6762..8b795df 100644 --- a/plugin/src/withReactNativeBatch.ts +++ b/plugin/src/withReactNativeBatch.ts @@ -10,12 +10,12 @@ import { withReactNativeBatchProjectBuildGradle } from './withReactNativeBatchPr import { withReactNativeBatchInfoPlist } from './withReactNativeBatchInfoPlist'; import { withReactNativeBatchAppDelegate } from './withReactNativeBatchAppDelegate'; -export type Props = { androidApiKey: string; iOSApiKey: string }; +export type Props = { androidApiKey: string; iosApiKey: string }; /** * Apply react-native-batch configuration for Expo SDK 42 projects. */ const withReactNativeBatch: ConfigPlugin = (config, props) => { - const _props = props || { androidApiKey: '', iOSApiKey: '' }; + const _props = props || { androidApiKey: '', iosApiKey: '' }; let newConfig = withGoogleServicesFile(config); newConfig = withClassPath(newConfig); diff --git a/plugin/src/withReactNativeBatchInfoPlist.ts b/plugin/src/withReactNativeBatchInfoPlist.ts index bc84861..9d9ee4b 100644 --- a/plugin/src/withReactNativeBatchInfoPlist.ts +++ b/plugin/src/withReactNativeBatchInfoPlist.ts @@ -5,7 +5,7 @@ export const modifyInfoPlist = ( infoPlist: InfoPlist, props: Props ): InfoPlist => { - infoPlist.BatchAPIKey = props.iOSApiKey; + infoPlist.BatchAPIKey = props.iosApiKey; return infoPlist; }; From a8537751ac49df9bc86c3e6703b51a723f490a30 Mon Sep 17 00:00:00 2001 From: Maxime Crampon Date: Thu, 20 Jan 2022 10:55:43 +0100 Subject: [PATCH 3/7] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20(expo)=20import=20Batc?= =?UTF-8?q?h=20earlier=20in=20AppDelegate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugin/src/fixtures/appDelegate.ts | 4 ++-- plugin/src/withReactNativeBatchAppDelegate.ts | 8 ++------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/plugin/src/fixtures/appDelegate.ts b/plugin/src/fixtures/appDelegate.ts index ede54b3..c8f86e2 100644 --- a/plugin/src/fixtures/appDelegate.ts +++ b/plugin/src/fixtures/appDelegate.ts @@ -157,6 +157,8 @@ static void InitializeFlipper(UIApplication *application) { export const appDelegateExpectedFixture = `#import "AppDelegate.h" +#import + #if defined(EX_DEV_MENU_ENABLED) @import EXDevMenu; #endif @@ -196,8 +198,6 @@ static void InitializeFlipper(UIApplication *application) { } #endif -#import - @interface AppDelegate () @property (nonatomic, strong) UMModuleRegistryAdapter *moduleRegistryAdapter; diff --git a/plugin/src/withReactNativeBatchAppDelegate.ts b/plugin/src/withReactNativeBatchAppDelegate.ts index a531f48..899d1fd 100644 --- a/plugin/src/withReactNativeBatchAppDelegate.ts +++ b/plugin/src/withReactNativeBatchAppDelegate.ts @@ -1,19 +1,15 @@ import { ConfigPlugin, withAppDelegate } from '@expo/config-plugins'; import { Props } from './withReactNativeBatch'; -const END_OF_HEADER = '@interface AppDelegate () '; const DID_FINISH_LAUNCHING_WITH_OPTIONS_DECLARATION = '- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions\n{'; -const IMPORT_BATCH = '#import \n\n'; +const IMPORT_BATCH = '\n\n#import \n'; const REGISTER_BATCH = '\n[RNBatch start];\n[BatchUNUserNotificationCenterDelegate registerAsDelegate];\n[BatchUNUserNotificationCenterDelegate sharedInstance].showForegroundNotifications = true;\n'; export const modifyAppDelegate = (contents: string) => { - const [header, __rest] = contents.split(END_OF_HEADER); - const newHeader = header.concat(IMPORT_BATCH).concat(END_OF_HEADER); - - contents = newHeader.concat(__rest); + contents = contents.replace('\n', IMPORT_BATCH); const [beforeDeclaration, afterDeclaration] = contents.split( DID_FINISH_LAUNCHING_WITH_OPTIONS_DECLARATION From fffb805e3bb2b759c16ed3aa1639b4100c61447e Mon Sep 17 00:00:00 2001 From: Maxime Crampon Date: Thu, 20 Jan 2022 14:04:05 +0100 Subject: [PATCH 4/7] :memo: (expo) update android installation doc --- readme/expo.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/readme/expo.md b/readme/expo.md index a647dd5..1358357 100644 --- a/readme/expo.md +++ b/readme/expo.md @@ -3,7 +3,7 @@ # Installation 1. Install using `yarn add @bam.tech/react-native-batch` or `npm i @bam.tech/react-native-batch` -2. Copy your google-services.json file at the root of your project (get it from the Firebase Console) +2. Copy your google-services.json file at the root of your project (get it from the Firebase Console) and link it in your app.json/app.config.js/app.config.ts under the key `googleServicesFile` of the android section. 3. In the app.json file add the plugin: ``` @@ -12,7 +12,7 @@ [ "@bam.tech/react-native-batch", { - "apiKey": + "androidApiKey": } ] ] @@ -36,7 +36,7 @@ module.exports = { ``` -5. Prepare your custom Expo client: `expo prebuild --clean` +5. Prepare your custom Expo client: `expo prebuild --clean`. This can be useful to debug and verify the plugin has executed correctly (compare with bare React Native configuration from the Batch doc) 6. Build your custom Expo client for Android: `expo run:android` for development When you are ready to go to production : [build your app with custom native code with EAS](https://docs.expo.dev/workflow/customizing/#releasing-apps-with-custom-native-code-to) From a27c8e30af5d70d1499aae92c29a6e5c4c460afd Mon Sep 17 00:00:00 2001 From: Maxime Crampon Date: Thu, 20 Jan 2022 14:38:09 +0100 Subject: [PATCH 5/7] :memo: (expo) add ios installation doc --- readme/expo.md | 66 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 51 insertions(+), 15 deletions(-) diff --git a/readme/expo.md b/readme/expo.md index 1358357..3b75857 100644 --- a/readme/expo.md +++ b/readme/expo.md @@ -1,42 +1,78 @@ -**Warning: This plugin is available only for Android for the moment. iOS version in progress...** +**Warning: The iOS part of this plugin is not compatible with Firebase cohabitation (on the iOS build), or other third-party libraries that may swizzle you application delegate file (ios/AppDelegate.m). To understand more or if you want to extend this plugin to cover the "manual integration" of Batch Push, read [this documentation](https://doc.batch.com/ios/advanced/manual-integration)** + +**Currently only supporting React Native >= 0.60.0. You should also use Expo SDK >= 42** # Installation +## Common steps (Android & iOS) + 1. Install using `yarn add @bam.tech/react-native-batch` or `npm i @bam.tech/react-native-batch` -2. Copy your google-services.json file at the root of your project (get it from the Firebase Console) and link it in your app.json/app.config.js/app.config.ts under the key `googleServicesFile` of the android section. -3. In the app.json file add the plugin: +2. In the app.json/app.config.js/app.config.ts file add the plugin: -``` +```json { "plugins": [ [ "@bam.tech/react-native-batch", { - "androidApiKey": + "androidApiKey": , + "iosApiKey": } ] ] } ``` -4. Create a react-native.config.js file at the root of your project and/or add the following lines: +## Additional Android steps -``` +1. Copy your google-services.json file at the root of your project (get it from the Firebase Console) and link it in your app.json/app.config.js/app.config.ts under the key `googleServicesFile` of the android section. + +2. Create a react-native.config.js file at the root of your project and/or add the following lines: + +```js module.exports = { dependencies: { - "@bam.tech/react-native-batch": { - platforms: { - android: { - packageInstance: "new RNBatchPackage(this.getApplication())", - }, + '@bam.tech/react-native-batch': { + platforms: { + android: { + packageInstance: 'new RNBatchPackage(this.getApplication())', }, }, + }, }, }; +``` + +## Additional iOS steps + +Add the following in your app code to enable push notifications, ideally the first view a user sees when opening the app: +```js +import { BatchPush } from '@bam.tech/react-native-batch'; + +... + +// Ask for the permission to display notifications +// The push token will automatically be fetched by the SDK +BatchPush.requestNotificationAuthorization(); + +// If you are using Batch plugin < 7.0.0 please use the following method or update the plugin. +// BatchPush.registerForRemoteNotifications(); + +// Alternatively, you can call requestNotificationAuthorization later +// But, you should always refresh your token on each application start +// This will make sure that even if your user's token changes, you still get notifications +// BatchPush.refreshToken(); ``` -5. Prepare your custom Expo client: `expo prebuild --clean`. This can be useful to debug and verify the plugin has executed correctly (compare with bare React Native configuration from the Batch doc) -6. Build your custom Expo client for Android: `expo run:android` for development +# Build and run locally + +1. Prepare your custom Expo client: `expo prebuild --clean`. This can be useful to debug and verify the plugin has executed correctly (compare with bare React Native configuration from the Batch doc) + +2. Build your custom Expo client for Android: `expo run:android` for development; or for iOS: `expo run:ios`. To force starting on physical device instead of a simulator, add the `-d` option. + +# Build with EAS + +When you are ready to go to production or to provide a new develoment client (for internal testing) containing your newly added custom native code: [build your app with custom native code with EAS](https://docs.expo.dev/workflow/customizing/#releasing-apps-with-custom-native-code-to) -When you are ready to go to production : [build your app with custom native code with EAS](https://docs.expo.dev/workflow/customizing/#releasing-apps-with-custom-native-code-to) +You will have to register every iOS device you plan on testing on with `eas device:create` (it has to be done before the build) From 82c43245f6b84bd79a2f950d413ee28a068a4fb4 Mon Sep 17 00:00:00 2001 From: Maxime Crampon Date: Fri, 21 Jan 2022 11:32:28 +0100 Subject: [PATCH 6/7] :memo: (expo) update ios warning on AppDelegate --- readme/expo.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme/expo.md b/readme/expo.md index 3b75857..5d490d0 100644 --- a/readme/expo.md +++ b/readme/expo.md @@ -1,4 +1,4 @@ -**Warning: The iOS part of this plugin is not compatible with Firebase cohabitation (on the iOS build), or other third-party libraries that may swizzle you application delegate file (ios/AppDelegate.m). To understand more or if you want to extend this plugin to cover the "manual integration" of Batch Push, read [this documentation](https://doc.batch.com/ios/advanced/manual-integration)** +**Warning: The iOS part of this plugin may not be compatible with a native Firebase module cohabitation, or other third-party libraries that may swizzle your application delegate file (ios/AppDelegate.m). To understand more or if you want to extend this plugin to cover the "manual integration" of Batch Push, read [this documentation](https://doc.batch.com/ios/advanced/manual-integration)** **Currently only supporting React Native >= 0.60.0. You should also use Expo SDK >= 42** From b963ecc0352e92a77f5b336e52bd0f0416fe4859 Mon Sep 17 00:00:00 2001 From: Maxime Crampon Date: Mon, 24 Jan 2022 10:56:50 +0100 Subject: [PATCH 7/7] :memo: (expo) delete ios comment about previous version --- readme/expo.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/readme/expo.md b/readme/expo.md index 5d490d0..2f4dfe6 100644 --- a/readme/expo.md +++ b/readme/expo.md @@ -56,9 +56,6 @@ import { BatchPush } from '@bam.tech/react-native-batch'; // The push token will automatically be fetched by the SDK BatchPush.requestNotificationAuthorization(); -// If you are using Batch plugin < 7.0.0 please use the following method or update the plugin. -// BatchPush.registerForRemoteNotifications(); - // Alternatively, you can call requestNotificationAuthorization later // But, you should always refresh your token on each application start // This will make sure that even if your user's token changes, you still get notifications