diff --git a/packages/expo-dev-launcher/CHANGELOG.md b/packages/expo-dev-launcher/CHANGELOG.md index 23da5064bf2729..191ed4d90be252 100644 --- a/packages/expo-dev-launcher/CHANGELOG.md +++ b/packages/expo-dev-launcher/CHANGELOG.md @@ -24,6 +24,7 @@ - Refactored network inspector code and add unit tests. ([#22669](https://github.com/expo/expo/pull/22669), [#22693](https://github.com/expo/expo/pull/22693) by [@kudo](https://github.com/kudo)) - Fixed `No compatible apps connected. JavaScript Debugging can only be used with the Hermes engine.` when using JavaScript debugger on Android. ([#20280](https://github.com/expo/expo/pull/20280) by [@kudo](https://github.com/kudo)) - Fix "multiple screens with the same name" warning on dev mode. ([#22847](https://github.com/expo/expo/pull/22847) by [@gabrieldonadel](https://github.com/gabrieldonadel)) +- [iOS] Fixed incorrect `Linking.getInitialURL()` value when launching through expo-dev-client's deep links. ([#22879](https://github.com/expo/expo/pull/22879) by [@kudo](https://github.com/kudo)) ### 💡 Others diff --git a/packages/expo-dev-launcher/ios/EXDevLauncherController.h b/packages/expo-dev-launcher/ios/EXDevLauncherController.h index ed820cb61b96d6..436a2e59573e2f 100644 --- a/packages/expo-dev-launcher/ios/EXDevLauncherController.h +++ b/packages/expo-dev-launcher/ios/EXDevLauncherController.h @@ -52,7 +52,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)clearRecentlyOpenedApps; -- (NSDictionary *)getLaunchOptions; +- (NSDictionary *)getLaunchOptions; - (EXManifestsManifest * _Nullable)appManifest; diff --git a/packages/expo-dev-launcher/ios/EXDevLauncherController.m b/packages/expo-dev-launcher/ios/EXDevLauncherController.m index dbbf365f1d31de..2b9b529b2a57e4 100644 --- a/packages/expo-dev-launcher/ios/EXDevLauncherController.m +++ b/packages/expo-dev-launcher/ios/EXDevLauncherController.m @@ -181,14 +181,28 @@ - (void)clearRecentlyOpenedApps - (NSDictionary *)getLaunchOptions; { + NSMutableDictionary *launchOptions = [self.launchOptions mutableCopy]; NSURL *deepLink = [self.pendingDeepLinkRegistry consumePendingDeepLink]; - if (!deepLink) { - return nil; + + if (deepLink) { + // Passes pending deep link to initialURL if any + launchOptions[UIApplicationLaunchOptionsURLKey] = deepLink; + } else if (launchOptions[UIApplicationLaunchOptionsURLKey] && [EXDevLauncherURLHelper isDevLauncherURL:launchOptions[UIApplicationLaunchOptionsURLKey]]) { + // Strips initialURL if it is from myapp://expo-development-client/?url=... + // That would make dev-launcher acts like a normal app. + launchOptions[UIApplicationLaunchOptionsURLKey] = nil; } - return @{ - UIApplicationLaunchOptionsURLKey: deepLink - }; + if ([launchOptions[UIApplicationLaunchOptionsUserActivityDictionaryKey][UIApplicationLaunchOptionsUserActivityTypeKey] isEqualToString:NSUserActivityTypeBrowsingWeb]) { + // Strips universal launch link if it is from https://expo-development-client/?url=... + // That would make dev-launcher acts like a normal app, though this case should rarely happen. + NSUserActivity *userActivity = launchOptions[UIApplicationLaunchOptionsUserActivityDictionaryKey][@"UIApplicationLaunchOptionsUserActivityKey"]; + if (userActivity.webpageURL && [EXDevLauncherURLHelper isDevLauncherURL:userActivity.webpageURL]) { + userActivity.webpageURL = nil; + } + } + + return launchOptions; } - (EXManifestsManifest *)appManifest diff --git a/packages/expo-dev-launcher/ios/ReactDelegateHandler/ExpoDevLauncherReactDelegateHandler.swift b/packages/expo-dev-launcher/ios/ReactDelegateHandler/ExpoDevLauncherReactDelegateHandler.swift index 8bd510395374cd..56ca2f0f16cff7 100644 --- a/packages/expo-dev-launcher/ios/ReactDelegateHandler/ExpoDevLauncherReactDelegateHandler.swift +++ b/packages/expo-dev-launcher/ios/ReactDelegateHandler/ExpoDevLauncherReactDelegateHandler.swift @@ -11,7 +11,6 @@ public class ExpoDevLauncherReactDelegateHandler: ExpoReactDelegateHandler, RCTB private weak var reactDelegate: ExpoReactDelegate? private var bridgeDelegate: RCTBridgeDelegate? - private var launchOptions: [AnyHashable : Any]? private var deferredRootView: EXDevLauncherDeferredRCTRootView? private var rootViewModuleName: String? private var rootViewInitialProperties: [AnyHashable : Any]? @@ -44,14 +43,15 @@ public class ExpoDevLauncherReactDelegateHandler: ExpoReactDelegateHandler, RCTB self.reactDelegate = reactDelegate self.bridgeDelegate = EXRCTBridgeDelegateInterceptor(bridgeDelegate: bridgeDelegate, interceptor: self) - self.launchOptions = launchOptions EXDevLauncherController.sharedInstance().autoSetupPrepare(self, launchOptions: launchOptions) if let sharedController = UpdatesControllerRegistry.sharedInstance.controller { // for some reason the swift compiler and bridge are having issues here EXDevLauncherController.sharedInstance().updatesInterface = sharedController } - return EXDevLauncherDeferredRCTBridge(delegate: self.bridgeDelegate!, launchOptions: self.launchOptions) + // swiftlint:disable force_unwrapping + return EXDevLauncherDeferredRCTBridge(delegate: self.bridgeDelegate!, launchOptions: launchOptions) + // swiftlint:enable force_unwrapping } public override func createRootView(reactDelegate: ExpoReactDelegate, bridge: RCTBridge, moduleName: String, initialProperties: [AnyHashable : Any]?) -> RCTRootView? { @@ -67,36 +67,30 @@ public class ExpoDevLauncherReactDelegateHandler: ExpoReactDelegateHandler, RCTB // MARK: RCTBridgeDelegate implementations + // swiftlint:disable implicitly_unwrapped_optional public func sourceURL(for bridge: RCTBridge!) -> URL! { return EXDevLauncherController.sharedInstance().sourceUrl() } + // swiftlint:enable implicitly_unwrapped_optional // MARK: EXDevelopmentClientControllerDelegate implementations public func devLauncherController(_ developmentClientController: EXDevLauncherController, didStartWithSuccess success: Bool) { - var launchOptions: [AnyHashable: Any] = [:] - - if let initialLaunchOptions = self.launchOptions { - for (key, value) in initialLaunchOptions { - launchOptions[key] = value - } - } - - for (key, value) in developmentClientController.getLaunchOptions() { - launchOptions[key] = value - } - - let bridge = RCTBridge(delegate: self.bridgeDelegate, launchOptions: launchOptions) + let bridge = RCTBridge(delegate: self.bridgeDelegate, launchOptions: developmentClientController.getLaunchOptions()) developmentClientController.appBridge = bridge + // swiftlint:disable force_unwrapping let rootView = RCTRootView(bridge: bridge!, moduleName: self.rootViewModuleName!, initialProperties: self.rootViewInitialProperties) + // swiftlint:enable force_unwrapping rootView.backgroundColor = self.deferredRootView?.backgroundColor ?? UIColor.white let window = getWindow() // NOTE: this order of assignment seems to actually have an effect on behaviour // direct assignment of window.rootViewController.view = rootView does not work - let rootViewController = self.reactDelegate?.createRootViewController() - rootViewController!.view = rootView + guard let rootViewController = self.reactDelegate?.createRootViewController() else { + fatalError("Invalid rootViewController returned from ExpoReactDelegate") + } + rootViewController.view = rootView window.rootViewController = rootViewController window.makeKeyAndVisible() @@ -112,9 +106,9 @@ public class ExpoDevLauncherReactDelegateHandler: ExpoReactDelegateHandler, RCTB if (window == nil) { window = UIApplication.shared.delegate?.window ?? nil } - if (window == nil) { + guard let window = window else { fatalError("Cannot find the current window.") } - return window! + return window } }