Repository navigation
fix(ios): forward open URLs to React Native linking before Expo subscribers - #530
Open
dustinsoftware wants to merge 1 commit into
Open
dustinsoftware wants to merge 1 commit into
dustinsoftware wants to merge 1 commit into
Conversation
…ribers ExpoHostRuntime.application(_:open:options:) joined the Expo app delegate subscribers and RCTLinkingManager with a short-circuiting ||, so a subscriber returning true kept the URL from React Native and the JS Linking 'url' event never fired. Evaluate the React Native result first and OR the subscribers in, as the continue userActivity overload already does.
Contributor
There was a problem hiding this comment.
🟢 Approval recommended
No active review issues are reported for the iOS Expo URL-forwarding change.
No open issues 🎉
What changed in this PR
Fixes URL forwarding in the iOS Expo host so an Expo subscriber returning true no longer prevents React Native linking from receiving the URL. This complements #525’s separate fix for warm deep links in scene-based hosts.
Changes:
- Calls React Native linking before Expo subscribers, ensuring both handlers run while preserving their combined return value.
- Adds a patch changeset for
@callstack/react-native-brownfield.
| File | Description |
|---|---|
packages/react-native-brownfield/ios/Expo/ExpoHostRuntime.swift |
Evaluates React Native linking before invoking Expo URL subscribers. |
.changeset/ios-open-url-linking-first.md |
Documents the URL-forwarding fix for a patch release. |
Review effort: Balanced
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is an upstream port of a patch in our Expo 58 app, but also applies to 57. It fixes some edge cases around opening deep links. This only affects dev builds.
I did not see an easy way to add tests for this.
Summary
ExpoHostRuntime.application(_:open:options:)evaluated the Expo app delegate subscribers first and joined them withRCTLinkingManagerthrough a short-circuiting||. When a subscriber returnedtrue, React Native never received the URL and the JSLinkingurlevent did not fire, so Expo Router / React Navigation did not navigate.In an Expo SDK 58 debug build with
expo-dev-client,ExpoDevLauncherAppDelegateSubscriberis such a subscriber:EXDevLauncherController.onDeepLink(_:options:)stores any non-launcher URL as a pending deep link and returnstruewhile its own React delegate handler has no running React host (expo-dev-launcher/ios/EXDevLauncherController.m:315-323,368-376). That is the normal state in a brownfield host, so every custom-scheme link opened while the app runs was lost.This change calls React Native linking first and ORs the subscriber result in. That is the order
application(_:continue:restorationHandler:)already uses a few lines below:expo-linkingstill records it), and the return value is still the OR of both.ReactNativeHostRuntime(vanilla) has no subscribers and is unchanged.patch,@callstack/react-native-brownfield) is included.Relation to #525
#525 changes the same line to call
BrownfieldLinking.open(...)and keeps the current order. The two changes are independent, but they touch the same line, so whichever merges second needs a one-line rebase. After both, the method reads:That combined form has been running as a local patch on top of 6.1.1 plus a #525 backport in an Expo SDK 58 / React Native 0.88 scene host.
Test plan
swiftc -parse. The change is two lines of Swift, so JS lint and typecheck do not cover it.expo-dev-clientinstalled): embed an Expo SDK 58 framework with Expo Router and a custom scheme. Forward URLs throughReactNativeBrownfield.shared.application(_:open:options:), show a React Native screen, and runxcrun simctl openurl booted "<scheme>://explore". Before the change the app stays on the current screen andRCTLinkingManager.application(_:open:options:)is not called. After it, Expo Router opens the Explore route. On a React Native 0.88 host that declaresUISceneConfigurations, this also needs fix(ios): deliver warm deep links in UIScene hosts on React Native 0.88 #525.expo-dev-client, and no subscriber in them returnstruefor an app URL, so the existing Detox suites cannot reach this branch. It could be covered by adding a small local Expo module toExpoApp58whoseExpoAppDelegateSubscriberreturnstruefor a test URL, and extending the warm deep link test from fix(ios): deliver warm deep links in UIScene hosts on React Native 0.88 #525 to open that URL and expect the route. That was left out to keep a test-only native module out of the example. It can be added if maintainers prefer.Other options that were considered
||(RCTLinkingManager... || expoDelegate...): that just moves the short-circuit, so subscribers such asexpo-linkingwould stop receiving URLs that React Native accepts. Evaluating both and then ORing them keeps every handler running, and it mirrors the existingcontinue userActivityoverload.falsewhen it does not own the React host: the bug belongs to brownfield's ordering, and any other subscriber that returnstruewould hit it the same way.🤖 Generated with Claude Code
https://claude.ai/code/session_01Gf7Xx7K4DTaYw2pLT2TJF7