diff --git a/ReactNativeNavigation.podspec b/ReactNativeNavigation.podspec index f0b9db98bb3..f189f90792f 100644 --- a/ReactNativeNavigation.podspec +++ b/ReactNativeNavigation.podspec @@ -4,6 +4,19 @@ package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) fabric_enabled = ENV['RCT_NEW_ARCH_ENABLED'] == '1' +# Detect if this is a Swift project by looking for user AppDelegate.swift files +dependency_paths = ['/node_modules/', '/Pods/', '/build/', '/Build/', '/DerivedData/'] +swift_project = Dir.glob('**/AppDelegate.swift') + .reject { |file| dependency_paths.any? { |path| file.include?(path) } } + .any? + +# Debug output +if swift_project + puts "ReactNativeNavigation: Swift AppDelegate detected - enabling Swift-compatible configuration" +else + puts "ReactNativeNavigation: Objective-C AppDelegate detected - using standard configuration" +end + Pod::Spec.new do |s| s.name = "ReactNativeNavigation" s.version = package['version'] @@ -21,14 +34,29 @@ Pod::Spec.new do |s| s.source = { :git => "https://github.com/wix/react-native-navigation.git", :tag => "#{s.version}" } s.source_files = 'lib/ios/**/*.{h,m,mm,cpp}' s.exclude_files = "lib/ios/ReactNativeNavigationTests/**/*.*", "lib/ios/OCMock/**/*.*" + # Only expose headers for Swift projects + if swift_project + s.public_header_files = [ + 'lib/ios/RNNAppDelegate.h' + ] + end end folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32 -DFOLLY_CFG_NO_COROUTINES=1' - s.pod_target_xcconfig = { + + # Base xcconfig settings + xcconfig_settings = { 'HEADER_SEARCH_PATHS' => '"$(PODS_ROOT)/boost" "$(PODS_ROOT)/boost-for-react-native" "$(PODS_ROOT)/RCT-Folly" "$(PODS_ROOT)/Headers/Private/React-Core" "$(PODS_ROOT)/Headers/Private/Yoga"', "CLANG_CXX_LANGUAGE_STANDARD" => "c++20", "OTHER_CPLUSPLUSFLAGS" => "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1", } + + # Only add DEFINES_MODULE for Swift projects + if swift_project + xcconfig_settings["DEFINES_MODULE"] = "YES" + end + + s.pod_target_xcconfig = xcconfig_settings if fabric_enabled install_modules_dependencies(s) diff --git a/autolink/fixtures/rn68/AppDelegate.mm.template b/autolink/fixtures/rn68/AppDelegate.mm.template deleted file mode 100644 index 27cde7ef817..00000000000 --- a/autolink/fixtures/rn68/AppDelegate.mm.template +++ /dev/null @@ -1,108 +0,0 @@ -#import "AppDelegate.h" - -#import -#import -#import - -#import - -#if RCT_NEW_ARCH_ENABLED -#import -#import -#import -#import -#import -#import - -#import - -@interface AppDelegate () { - RCTTurboModuleManager *_turboModuleManager; - RCTSurfacePresenterBridgeAdapter *_bridgeAdapter; - std::shared_ptr _reactNativeConfig; - facebook::react::ContextContainer::Shared _contextContainer; -} -@end -#endif - -@implementation AppDelegate - -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions -{ - RCTAppSetupPrepareApp(application); - - RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; - -#if RCT_NEW_ARCH_ENABLED - _contextContainer = std::make_shared(); - _reactNativeConfig = std::make_shared(); - _contextContainer->insert("ReactNativeConfig", _reactNativeConfig); - _bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:bridge contextContainer:_contextContainer]; - bridge.surfacePresenter = _bridgeAdapter.surfacePresenter; -#endif - - UIView *rootView = RCTAppSetupDefaultRootView(bridge, @"app", nil); - - if (@available(iOS 13.0, *)) { - rootView.backgroundColor = [UIColor systemBackgroundColor]; - } else { - rootView.backgroundColor = [UIColor whiteColor]; - } - - self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; - UIViewController *rootViewController = [UIViewController new]; - rootViewController.view = rootView; - self.window.rootViewController = rootViewController; - [self.window makeKeyAndVisible]; - return YES; -} - -- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge -{ -#if DEBUG - return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; -#else - return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; -#endif -} - -#if RCT_NEW_ARCH_ENABLED - -#pragma mark - RCTCxxBridgeDelegate - -- (std::unique_ptr)jsExecutorFactoryForBridge:(RCTBridge *)bridge -{ - _turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge - delegate:self - jsInvoker:bridge.jsCallInvoker]; - return RCTAppSetupDefaultJsExecutorFactory(bridge, _turboModuleManager); -} - -#pragma mark RCTTurboModuleManagerDelegate - -- (Class)getModuleClassFromName:(const char *)name -{ - return RCTCoreModulesClassProvider(name); -} - -- (std::shared_ptr)getTurboModule:(const std::string &)name - jsInvoker:(std::shared_ptr)jsInvoker -{ - return nullptr; -} - -- (std::shared_ptr)getTurboModule:(const std::string &)name - initParams: - (const facebook::react::ObjCTurboModule::InitParams &)params -{ - return nullptr; -} - -- (id)getModuleInstanceFromClass:(Class)moduleClass -{ - return RCTAppSetupDefaultModuleFromClass(moduleClass); -} - -#endif - -@end diff --git a/autolink/fixtures/rn69/AppDelegate.mm.template b/autolink/fixtures/rn69/AppDelegate.mm.template deleted file mode 100644 index d99dc5c3608..00000000000 --- a/autolink/fixtures/rn69/AppDelegate.mm.template +++ /dev/null @@ -1,133 +0,0 @@ -#import "AppDelegate.h" - -#import -#import -#import - -#import - -#if RCT_NEW_ARCH_ENABLED -#import -#import -#import -#import -#import -#import - -#import - -static NSString *const kRNConcurrentRoot = @"concurrentRoot"; - -@interface AppDelegate () { - RCTTurboModuleManager *_turboModuleManager; - RCTSurfacePresenterBridgeAdapter *_bridgeAdapter; - std::shared_ptr _reactNativeConfig; - facebook::react::ContextContainer::Shared _contextContainer; -} -@end -#endif - -@implementation AppDelegate - -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions -{ - RCTAppSetupPrepareApp(application); - - RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; - -#if RCT_NEW_ARCH_ENABLED - _contextContainer = std::make_shared(); - _reactNativeConfig = std::make_shared(); - _contextContainer->insert("ReactNativeConfig", _reactNativeConfig); - _bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:bridge contextContainer:_contextContainer]; - bridge.surfacePresenter = _bridgeAdapter.surfacePresenter; -#endif - - NSDictionary *initProps = [self prepareInitialProps]; - UIView *rootView = RCTAppSetupDefaultRootView(bridge, @"app", initProps); - - if (@available(iOS 13.0, *)) { - rootView.backgroundColor = [UIColor systemBackgroundColor]; - } else { - rootView.backgroundColor = [UIColor whiteColor]; - } - - self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; - UIViewController *rootViewController = [UIViewController new]; - rootViewController.view = rootView; - self.window.rootViewController = rootViewController; - [self.window makeKeyAndVisible]; - return YES; -} - -/// This method controls whether the `concurrentRoot`feature of React18 is turned on or off. -/// -/// @see: https://reactjs.org/blog/2022/03/29/react-v18.html -/// @note: This requires to be rendering on Fabric (i.e. on the New Architecture). -/// @return: `true` if the `concurrentRoot` feture is enabled. Otherwise, it returns `false`. -- (BOOL)concurrentRootEnabled -{ - // Switch this bool to turn on and off the concurrent root - return true; -} - -- (NSDictionary *)prepareInitialProps -{ - NSMutableDictionary *initProps = [NSMutableDictionary new]; - -#ifdef RCT_NEW_ARCH_ENABLED - initProps[kRNConcurrentRoot] = @([self concurrentRootEnabled]); -#endif - - return initProps; -} - -- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge -{ -#if DEBUG - return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; -#else - return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; -#endif -} - -#if RCT_NEW_ARCH_ENABLED - -#pragma mark - RCTCxxBridgeDelegate - -- (std::unique_ptr)jsExecutorFactoryForBridge:(RCTBridge *)bridge -{ - _turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge - delegate:self - jsInvoker:bridge.jsCallInvoker]; - return RCTAppSetupDefaultJsExecutorFactory(bridge, _turboModuleManager); -} - -#pragma mark RCTTurboModuleManagerDelegate - -- (Class)getModuleClassFromName:(const char *)name -{ - return RCTCoreModulesClassProvider(name); -} - -- (std::shared_ptr)getTurboModule:(const std::string &)name - jsInvoker:(std::shared_ptr)jsInvoker -{ - return nullptr; -} - -- (std::shared_ptr)getTurboModule:(const std::string &)name - initParams: - (const facebook::react::ObjCTurboModule::InitParams &)params -{ - return nullptr; -} - -- (id)getModuleInstanceFromClass:(Class)moduleClass -{ - return RCTAppSetupDefaultModuleFromClass(moduleClass); -} - -#endif - -@end diff --git a/autolink/fixtures/rn71/AppDelegate.mm.template b/autolink/fixtures/rn77/AppDelegate.mm.template similarity index 99% rename from autolink/fixtures/rn71/AppDelegate.mm.template rename to autolink/fixtures/rn77/AppDelegate.mm.template index 903e9befdbc..b85dd4ad8df 100644 --- a/autolink/fixtures/rn71/AppDelegate.mm.template +++ b/autolink/fixtures/rn77/AppDelegate.mm.template @@ -33,4 +33,4 @@ return true; } -@end +@end \ No newline at end of file diff --git a/autolink/fixtures/rn77/AppDelegate.swift.template b/autolink/fixtures/rn77/AppDelegate.swift.template new file mode 100644 index 00000000000..1b0c273a58a --- /dev/null +++ b/autolink/fixtures/rn77/AppDelegate.swift.template @@ -0,0 +1,30 @@ +import UIKit +import React +import React_RCTAppDelegate +import ReactAppDependencyProvider + +@main +class AppDelegate: RCTAppDelegate { + override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { + self.moduleName = "app" + self.dependencyProvider = RCTAppDependencyProvider() + + // You can add your custom initial props in the dictionary below. + // They will be passed down to the ViewController used by React Native. + self.initialProps = [:] + + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } + + override func sourceURL(for bridge: RCTBridge) -> URL? { + self.bundleURL() + } + + override func bundleURL() -> URL? { +#if DEBUG + RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index") +#else + Bundle.main.url(forResource: "main", withExtension: "jsbundle") +#endif + } +} \ No newline at end of file diff --git a/autolink/postlink/__snapshots__/appDelegateLinker.test.js.snap b/autolink/postlink/__snapshots__/appDelegateLinker.test.js.snap index 3bd71277c32..0331cedf2d4 100644 --- a/autolink/postlink/__snapshots__/appDelegateLinker.test.js.snap +++ b/autolink/postlink/__snapshots__/appDelegateLinker.test.js.snap @@ -1,55 +1,17 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`appDelegateLinker should work for RN 0.68 1`] = ` +exports[`appDelegateLinker should work for RN 0.77 with Objective-C 1`] = ` "#import "AppDelegate.h" #import -#import #import -#import - -#if RCT_NEW_ARCH_ENABLED -#import -#import -#import -#import -#import -#import - -#import - -@interface AppDelegate () { - RCTTurboModuleManager *_turboModuleManager; - RCTSurfacePresenterBridgeAdapter *_bridgeAdapter; - std::shared_ptr _reactNativeConfig; - facebook::react::ContextContainer::Shared _contextContainer; -} -@end -#endif - @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - RCTAppSetupPrepareApp(application); - - RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; -[ReactNativeNavigation bootstrapWithBridge:bridge]; - -#if RCT_NEW_ARCH_ENABLED - _contextContainer = std::make_shared(); - _reactNativeConfig = std::make_shared(); - _contextContainer->insert("ReactNativeConfig", _reactNativeConfig); - _bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:bridge contextContainer:_contextContainer]; - bridge.surfacePresenter = _bridgeAdapter.surfacePresenter; -#endif - - - - - return YES; + return [super application:application didFinishLaunchingWithOptions:launchOptions]; } - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge @@ -61,210 +23,48 @@ exports[`appDelegateLinker should work for RN 0.68 1`] = ` #endif } -#if RCT_NEW_ARCH_ENABLED - -#pragma mark - RCTCxxBridgeDelegate - -- (std::unique_ptr)jsExecutorFactoryForBridge:(RCTBridge *)bridge -{ - _turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge - delegate:self - jsInvoker:bridge.jsCallInvoker]; - return RCTAppSetupDefaultJsExecutorFactory(bridge, _turboModuleManager); -} - -#pragma mark RCTTurboModuleManagerDelegate - -- (Class)getModuleClassFromName:(const char *)name -{ - return RCTCoreModulesClassProvider(name); -} - -- (std::shared_ptr)getTurboModule:(const std::string &)name - jsInvoker:(std::shared_ptr)jsInvoker -{ - return nullptr; -} - -- (std::shared_ptr)getTurboModule:(const std::string &)name - initParams: - (const facebook::react::ObjCTurboModule::InitParams &)params -{ - return nullptr; -} - -- (id)getModuleInstanceFromClass:(Class)moduleClass -{ - return RCTAppSetupDefaultModuleFromClass(moduleClass); -} - -#endif - -@end -" -`; - -exports[`appDelegateLinker should work for RN 0.69 1`] = ` -"#import "AppDelegate.h" -#import - -#import -#import - -#import - -#if RCT_NEW_ARCH_ENABLED -#import -#import -#import -#import -#import -#import - -#import - -static NSString *const kRNConcurrentRoot = @"concurrentRoot"; - -@interface AppDelegate () { - RCTTurboModuleManager *_turboModuleManager; - RCTSurfacePresenterBridgeAdapter *_bridgeAdapter; - std::shared_ptr _reactNativeConfig; - facebook::react::ContextContainer::Shared _contextContainer; -} -@end -#endif - -@implementation AppDelegate - -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions -{ - RCTAppSetupPrepareApp(application); - - RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; -[ReactNativeNavigation bootstrapWithBridge:bridge]; - -#if RCT_NEW_ARCH_ENABLED - _contextContainer = std::make_shared(); - _reactNativeConfig = std::make_shared(); - _contextContainer->insert("ReactNativeConfig", _reactNativeConfig); - _bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:bridge contextContainer:_contextContainer]; - bridge.surfacePresenter = _bridgeAdapter.surfacePresenter; -#endif - - - - - - return YES; -} - /// This method controls whether the \`concurrentRoot\`feature of React18 is turned on or off. /// /// @see: https://reactjs.org/blog/2022/03/29/react-v18.html /// @note: This requires to be rendering on Fabric (i.e. on the New Architecture). -/// @return: \`true\` if the \`concurrentRoot\` feture is enabled. Otherwise, it returns \`false\`. +/// @return: \`true\` if the \`concurrentRoot\` feature is enabled. Otherwise, it returns \`false\`. - (BOOL)concurrentRootEnabled { - // Switch this bool to turn on and off the concurrent root return true; } -- (NSDictionary *)prepareInitialProps -{ - NSMutableDictionary *initProps = [NSMutableDictionary new]; - -#ifdef RCT_NEW_ARCH_ENABLED - initProps[kRNConcurrentRoot] = @([self concurrentRootEnabled]); -#endif - - return initProps; -} - -- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge -{ -#if DEBUG - return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; -#else - return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; -#endif -} - -#if RCT_NEW_ARCH_ENABLED - -#pragma mark - RCTCxxBridgeDelegate - -- (std::unique_ptr)jsExecutorFactoryForBridge:(RCTBridge *)bridge -{ - _turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge - delegate:self - jsInvoker:bridge.jsCallInvoker]; - return RCTAppSetupDefaultJsExecutorFactory(bridge, _turboModuleManager); -} - -#pragma mark RCTTurboModuleManagerDelegate - -- (Class)getModuleClassFromName:(const char *)name -{ - return RCTCoreModulesClassProvider(name); -} - -- (std::shared_ptr)getTurboModule:(const std::string &)name - jsInvoker:(std::shared_ptr)jsInvoker -{ - return nullptr; -} - -- (std::shared_ptr)getTurboModule:(const std::string &)name - initParams: - (const facebook::react::ObjCTurboModule::InitParams &)params -{ - return nullptr; -} - -- (id)getModuleInstanceFromClass:(Class)moduleClass -{ - return RCTAppSetupDefaultModuleFromClass(moduleClass); -} - -#endif - -@end -" +@end " `; -exports[`appDelegateLinker should work for RN 0.71 1`] = ` -"#import "AppDelegate.h" -#import +exports[`appDelegateLinker should work for RN 0.77 with Swift 1`] = ` +"import UIKit +import React +import ReactNativeNavigation +import ReactAppDependencyProvider -#import +@main +class AppDelegate: RNNAppDelegate { + override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { + self.moduleName = "app" + self.dependencyProvider = RCTAppDependencyProvider() -@implementation AppDelegate + // You can add your custom initial props in the dictionary below. + // They will be passed down to the ViewController used by React Native. + self.initialProps = [:] -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions -{ - - return [super application:application didFinishLaunchingWithOptions:launchOptions]; -} + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } -- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge -{ + override func sourceURL(for bridge: RCTBridge) -> URL? { + self.bundleURL() + } + + override func bundleURL() -> URL? { #if DEBUG - return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; + RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index") #else - return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; + Bundle.main.url(forResource: "main", withExtension: "jsbundle") #endif -} - -/// This method controls whether the \`concurrentRoot\`feature of React18 is turned on or off. -/// -/// @see: https://reactjs.org/blog/2022/03/29/react-v18.html -/// @note: This requires to be rendering on Fabric (i.e. on the New Architecture). -/// @return: \`true\` if the \`concurrentRoot\` feature is enabled. Otherwise, it returns \`false\`. -- (BOOL)concurrentRootEnabled -{ - return true; -} - -@end -" + } +} " `; diff --git a/autolink/postlink/appDelegateLinker.js b/autolink/postlink/appDelegateLinker.js index e57b0de48b3..35ff99e10cb 100644 --- a/autolink/postlink/appDelegateLinker.js +++ b/autolink/postlink/appDelegateLinker.js @@ -1,6 +1,7 @@ // @ts-check var fs = require('fs'); var path = require('./path'); +var nodePath = require('path'); var { warnn, logn, infon, debugn, errorn } = require('./log'); class AppDelegateLinker { @@ -21,34 +22,46 @@ class AppDelegateLinker { logn('Linking AppDelegate...'); - var appDelegateContents = fs.readFileSync(this.appDelegatePath, 'utf8'); + // New flow for Swift + if (nodePath.extname(this.appDelegatePath) === '.swift') { + debugn('Entering Swift flow ...'); + var appDelegateContents = fs.readFileSync(this.appDelegatePath, 'utf8'); + appDelegateContents = this._extendRNNAppDelegateSwift(appDelegateContents); + fs.writeFileSync(this.appDelegatePath, appDelegateContents); + this.removeUnneededImportsSuccess = true + this.removeApplicationLaunchContentSuccess = true + + } else { // Old flow for Objective-C + debugn('Entering Objective-C flow ...'); + var appDelegateContents = fs.readFileSync(this.appDelegatePath, 'utf8'); + + if (this.appDelegateHeaderPath) { + var appDelegateHeaderContents = fs.readFileSync(this.appDelegateHeaderPath, 'utf8'); + appDelegateHeaderContents = this._extendRNNAppDelegate(appDelegateHeaderContents); + fs.writeFileSync(this.appDelegateHeaderPath, appDelegateHeaderContents); + } - if (this.appDelegateHeaderPath) { - var appDelegateHeaderContents = fs.readFileSync(this.appDelegateHeaderPath, 'utf8'); - appDelegateHeaderContents = this._extendRNNAppDelegate(appDelegateHeaderContents); - fs.writeFileSync(this.appDelegateHeaderPath, appDelegateHeaderContents); - } + try { + appDelegateContents = this._removeUnneededImports(appDelegateContents); + this.removeUnneededImportsSuccess = true; + } catch (e) { + errorn(' ' + e.message); + } - try { - appDelegateContents = this._removeUnneededImports(appDelegateContents); - this.removeUnneededImportsSuccess = true; - } catch (e) { - errorn(' ' + e.message); - } + appDelegateContents = this._importNavigation(appDelegateContents); - appDelegateContents = this._importNavigation(appDelegateContents); + appDelegateContents = this._bootstrapNavigation(appDelegateContents); - appDelegateContents = this._bootstrapNavigation(appDelegateContents); + try { + appDelegateContents = this._removeApplicationLaunchContent(appDelegateContents); + this.removeApplicationLaunchContentSuccess = true; + } catch (e) { + errorn(' ' + e.message); + } - try { - appDelegateContents = this._removeApplicationLaunchContent(appDelegateContents); - this.removeApplicationLaunchContentSuccess = true; - } catch (e) { - errorn(' ' + e.message); + fs.writeFileSync(this.appDelegatePath, appDelegateContents); } - fs.writeFileSync(this.appDelegatePath, appDelegateContents); - if (this.removeUnneededImportsSuccess && this.removeApplicationLaunchContentSuccess) { infon('AppDelegate linked successfully!\n'); } else { @@ -178,6 +191,19 @@ class AppDelegateLinker { _doesImportNavigation(content) { return /#import\s+\/.test(content); } + + // SWIFT implementation + _extendRNNAppDelegateSwift(content) { + return content + .replace( + /import React_RCTAppDelegate/, + 'import ReactNativeNavigation' + ) + .replace( + /class AppDelegate: RCTAppDelegate/, + 'class AppDelegate: RNNAppDelegate' + ) + } } module.exports = AppDelegateLinker; diff --git a/autolink/postlink/appDelegateLinker.test.js b/autolink/postlink/appDelegateLinker.test.js index 7916e79a2c9..40f1eed26c8 100644 --- a/autolink/postlink/appDelegateLinker.test.js +++ b/autolink/postlink/appDelegateLinker.test.js @@ -21,9 +21,9 @@ jest.mock('../postlink/log', () => ({ */ describe('appDelegateLinker', () => { - beforeEach(() => {}); + beforeEach(() => { }); - it('should work for RN 0.68', () => { + it('should work for RN 0.77 with Objective-C', () => { jest.mock('../postlink/path', () => { const { copyFileSync } = require('fs'); const { tmpdir } = require('os'); @@ -32,7 +32,7 @@ describe('appDelegateLinker', () => { const tmpAppDelegatePath = path.resolve(tmpdir(), 'rnn-tests_AppDelegate.mm'); copyFileSync( - path.resolve('autolink/fixtures/rn68/AppDelegate.mm.template'), + path.resolve('autolink/fixtures/rn77/AppDelegate.mm.template'), tmpAppDelegatePath ); @@ -49,42 +49,16 @@ describe('appDelegateLinker', () => { expect(appDelegateContent).toMatchSnapshot(); }); - it('should work for RN 0.69', () => { + it('should work for RN 0.77 with Swift', () => { jest.mock('../postlink/path', () => { const { copyFileSync } = require('fs'); const { tmpdir } = require('os'); const path = require('path'); - const tmpAppDelegatePath = path.resolve(tmpdir(), 'rnn-tests_AppDelegate.mm'); - - copyFileSync( - path.resolve('autolink/fixtures/rn69/AppDelegate.mm.template'), - tmpAppDelegatePath - ); - - return { - appDelegate: tmpAppDelegatePath, - }; - }); - - const AppDelegateLinker = require('./appDelegateLinker'); - const linker = new AppDelegateLinker(); - - linker.link(); - const appDelegateContent = fs.readFileSync(linker.appDelegatePath, 'utf8'); - expect(appDelegateContent).toMatchSnapshot(); - }); - - it('should work for RN 0.71', () => { - jest.mock('../postlink/path', () => { - const { copyFileSync } = require('fs'); - const { tmpdir } = require('os'); - const path = require('path'); - - const tmpAppDelegatePath = path.resolve(tmpdir(), 'rnn-tests_AppDelegate.mm'); + const tmpAppDelegatePath = path.resolve(tmpdir(), 'rnn-tests_AppDelegate.swift'); copyFileSync( - path.resolve('autolink/fixtures/rn71/AppDelegate.mm.template'), + path.resolve('autolink/fixtures/rn77/AppDelegate.swift.template'), tmpAppDelegatePath ); diff --git a/autolink/postlink/path.js b/autolink/postlink/path.js index 3b5055b0098..62c6858d76b 100644 --- a/autolink/postlink/path.js +++ b/autolink/postlink/path.js @@ -2,6 +2,7 @@ var glob = require('glob'); var ignoreFolders = { ignore: ['node_modules/**', '**/build/**', '**/Build/**', '**/DerivedData/**', '**/*-tvOS*/**'], }; +var { warnn, infon, debugn } = require('./log'); exports.mainActivityJava = glob.sync('**/MainActivity.java', ignoreFolders)[0]; exports.mainActivityKotlin = glob.sync('**/MainActivity.kt', ignoreFolders)[0]; @@ -10,10 +11,28 @@ exports.mainApplicationJava = mainApplicationJava; exports.rootGradle = mainApplicationJava.replace(/android\/app\/.*\.java/, 'android/build.gradle'); var reactNativeVersion = require('../../../react-native/package.json').version; + +infon('Locating the AppDelegate.mm file ...'); exports.appDelegate = glob.sync( - reactNativeVersion < '0.68.0' ? '**/AppDelegate.m' : '**/AppDelegate.mm', + '**/AppDelegate.mm', ignoreFolders )[0]; -exports.appDelegateHeader = glob.sync('**/AppDelegate.h', ignoreFolders)[0]; + +if (exports.appDelegate === undefined) { + warnn('AppDelegate.mm file not found, looking for AppDelegate.swift ...'); + exports.appDelegate = glob.sync( + '**/AppDelegate.swift', + ignoreFolders + )[0]; + + if (exports.appDelegate !== undefined) { + debugn('Found AppDelegate.swift'); + } + +} else { + debugn('Found AppDelegate.mm'); + exports.appDelegateHeader = glob.sync('**/AppDelegate.h', ignoreFolders)[0]; +} + exports.podFile = glob.sync('**/Podfile', ignoreFolders)[0]; exports.plist = glob.sync('**/info.plist', ignoreFolders)[0];