Skip to content

[pull] main from expo:main#899

Merged
pull[bot] merged 2 commits into
code:mainfrom
expo:main
May 25, 2026
Merged

[pull] main from expo:main#899
pull[bot] merged 2 commits into
code:mainfrom
expo:main

Conversation

@pull
Copy link
Copy Markdown

@pull pull Bot commented May 25, 2026

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

kaihirota and others added 2 commits May 25, 2026 10:30
#46185)

# Why

The `defaultLaunchURL` plugin option ([added in SDK 56.0.0,
#44419](#44419)) is documented as:

> Instead of navigating to launcher screen launch directly into this
URL. If `launchMode` is set to `most-recent`, then launcher will use the
`defaultLaunchURL` as a fallback.

On iOS this is currently a silent no-op on cold start: the plugin writes
the plist key correctly, but the native fallback path never reads it.

# How

`packages/expo-dev-launcher/ios/EXDevLauncherController.m:255` (`start:`
method) calls:

```objc
[self useDefaultLaunchUrlFallback];
```

But `useDefaultLaunchUrlFallback` is a `@property (nonatomic) BOOL`
declared at line 60 — **not a method**. Objective-C resolves the bare
message send to the property's getter, reads the BOOL, and discards the
result. No fallback ever runs.

The intent was clearly to invoke the existing method
`launchDefaultUrlFallbackOrNavigateToLauncher` (defined immediately
below at line 262, and already used elsewhere as the error handler at
lines 214/251/307).

## Repro

1. Configure `defaultLaunchURL` in `expo-dev-client` plugin config:
   ```jsonc
   ["expo-dev-client", {
     "launchMode": "most-recent",
     "defaultLaunchURL": "http://localhost:8081"
   }]
   ```
2. Confirm the plist key is written: `DEV_CLIENT_DEFAULT_LAUNCHER_URL`
appears in `Info.plist` ✓
3. Install on a **fresh simulator** (no recently-opened app cached →
`_lastOpenedAppUrl == nil`).
4. Launch the dev build.
5. **Expected:** App loads `http://localhost:8081` directly.
6. **Actual:** Dev launcher screen is shown; `defaultLaunchURL` is never
read.

This was discovered while configuring SDK 56 dev-launcher options for CI
snapshot tests, where the launcher UI changes between simulator boots
make a deterministic `defaultLaunchURL`-based bypass essential.

## Fix

```diff
-  [self useDefaultLaunchUrlFallback];
+  if (useDefaultLaunchUrlFallback) {
+    [self launchDefaultUrlFallbackOrNavigateToLauncher];
+  } else {
+    [self navigateToLauncher];
+  }
```

A couple of subtleties worth noting:

- The local `useDefaultLaunchUrlFallback` variable (line 241) is
intentionally distinct from `self.useDefaultLaunchUrlFallback`: it gets
forced to `NO` on line 245 when `hasGrantedNetworkPermission` is false.
The existing `launchDefaultUrlFallbackOrNavigateToLauncher` method
checks the property (not the local), so we gate on the local first to
preserve the "no network permission → don't try the load" intent.
Without this guard, calling the method directly would kick off a
`loadApp` to `defaultLaunchURL` even when the user has denied
local-network access.
- When `useDefaultLaunchUrlFallback` is false (no permission or no
configured URL), we explicitly call `[self navigateToLauncher]`.
Previously the code fell through, relying on the view controller's own
rendering of the launcher — that's incidentally what users see today
because of the no-op bug, but it's not explicit. Making it explicit
prevents future regressions from re-introducing a silent no-op.

## Test Plan

- [x] Configure `defaultLaunchURL` in a dev-client app, install on fresh
simulator → app now loads `http://localhost:8081` directly without
showing the launcher.
- [x] Configure `defaultLaunchURL` and open the app after a previous
successful launch (warm start with cached `_lastOpenedAppUrl`) →
unchanged: tries cached URL first, falls back to `defaultLaunchURL` on
failure via existing path at line 251.
- [x] No `defaultLaunchURL` configured → unchanged: launcher screen is
shown.
- [x] (Device, not simulator) Network permission denied +
`defaultLaunchURL` set → unchanged: launcher shown, no unwanted network
request.

> Note: tested against `expo-dev-launcher` 56.0.14 installed in a
downstream Expo SDK 56 app (RN 0.85.3) — not yet exercised via
`apps/bare-expo` as recommended in CONTRIBUTING.md. Happy to re-test
there if requested.

## Checklist

- [x] PR title and commit message follow `[platform][api] Title`
convention per CONTRIBUTING.md
- [x] Changelog entry added under `## Unpublished` → `### 🐛 Bug fixes`
in `packages/expo-dev-launcher/CHANGELOG.md`
- [x] No TypeScript changes (Objective-C only), so no `pnpm build`
needed
- [x] No breaking changes — purely fixes broken behavior to match
documented intent

---------

Co-authored-by: Kai Hirota <kai@koilabs.io>
Co-authored-by: HubertBer <115428831+HubertBer@users.noreply.github.com>
)

## Why

`expo-widgets` currently hardcodes the generated widget extension target
deployment version to `16.4`. Apps that set a newer
`ios.deploymentTarget` can therefore end up with the containing app
target and `ExpoWidgetsTarget` using different deployment targets.

This is especially relevant for apps whose dependencies require a newer
iOS version, because native objects may be built for the newer app
target and then linked into a widget extension still targeting `16.4`,
producing warnings such as:

```text
object file was built for newer 'iOS-simulator' version (17.0) than being linked (16.4)
```

Closes #46192

## How

Use `config.ios?.deploymentTarget` for the generated widget extension
target when it is configured, and keep the existing `16.4` fallback
otherwise.

## Test Plan

- `git diff --check`
- Verified with the minimal repro from #46192 that `ExpoWidgetsTarget`
changes from `IPHONEOS_DEPLOYMENT_TARGET = 16.4` to
`IPHONEOS_DEPLOYMENT_TARGET = 17.0` after this patch.
@pull pull Bot locked and limited conversation to collaborators May 25, 2026
@pull pull Bot added the ⤵️ pull label May 25, 2026
@pull pull Bot merged commit 67a7929 into code:main May 25, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants