feat(mobile): upgrade to Expo SDK 57 / React Native 0.86.3 #150

Merged
ullrich merged 1 commit from upgrade-expo-sdk-57 into main 2026-09-12 11:24:10 +00:00
Owner

Moves apps/mobile from Expo SDK 56 to SDK 57. All 25 version specs are taken from SDK 57's own bundledNativeModules.json rather than chosen by hand.

Headline moves: expo ~56.0.12 → ~57.0.22 · react-native 0.85.3 → 0.86.3 · expo-router ~56.2.19 → ~57.0.21 · react-native-reanimated 4.3 → 4.5.1 · react-native-worklets 0.8.3 → 0.10.1, plus every expo-* package.

Dependency decisions

  • react unchanged. SDK 57 pins react to exactly 19.2.3, which is what the catalog already carries after #147. No change needed.
  • @types/react pinned to ~19.2.4, mobile-only. Unlike react, it is not in pnpm-workspace.yaml's overrides block, so a per-app pin actually takes effect. journal, planner and @trails-cool/ui keep the catalog's 19.3.0; mobile does not depend on @trails-cool/ui, so the split does not produce clashing React types. This clears the last expo install --check finding.
  • @sentry/react-native deliberately left on 8.x. SDK 57's bundled list names ~7.11.0 — a major downgrade. The package is already in expo.install.exclude for exactly this reason. Don't let expo install --fix "correct" it.

Unmanaged native deps, checked against RN 0.86 before starting

Package Peer OK
@maplibre/maplibre-react-native react-native >=0.80.0
@sentry/react-native 8.26.0 react-native >=0.65.0
@gorhom/bottom-sheet reanimated >=4.0.0- (57 ships 4.5.1)

expo-router/unstable-native-tabs was the main risk, being an unstable API across a major. It still ships in 57, and NativeTabs.Trigger still exposes .Label/.Icon/.Badge with the same sf/md {default,selected} shape, so app/(tabs)/_layout.tsx is unchanged.

android/ and ios/ are gitignored (CNG), so there is no hand-edited native code to reconcile.

Verification

Check Result
expo install --check Dependencies are up to date (was 9 outdated)
pnpm typecheck / lint / test 14/14 · 14/14 · 12/12
expo prebuild --clean all config plugins regenerate
gradle assembleDebug SUCCESSFUL — 758 tasks, 4 ABIs
Install over SDK 56 build in-place upgrade, no uninstall
App boot on RN 0.86 boots, session intact
NativeTabs layout all 4 tabs + icons render
MapLibre map (#148) renders full-bleed on RN 0.86

Verified on an Android emulator (Pixel 6 Pro, API 34) after rebasing onto main so the map from #148 was present — the SDK upgrade is confirmed against the new map screen, not just against the old placeholder.

🤖 Generated with Claude Code

Moves `apps/mobile` from Expo SDK 56 to SDK 57. All 25 version specs are taken from SDK 57's own `bundledNativeModules.json` rather than chosen by hand. **Headline moves:** `expo` ~56.0.12 → ~57.0.22 · `react-native` 0.85.3 → 0.86.3 · `expo-router` ~56.2.19 → ~57.0.21 · `react-native-reanimated` 4.3 → 4.5.1 · `react-native-worklets` 0.8.3 → 0.10.1, plus every `expo-*` package. ### Dependency decisions - **`react` unchanged.** SDK 57 pins react to exactly `19.2.3`, which is what the catalog already carries after #147. No change needed. - **`@types/react` pinned to `~19.2.4`, mobile-only.** Unlike `react`, it is *not* in `pnpm-workspace.yaml`'s `overrides` block, so a per-app pin actually takes effect. journal, planner and `@trails-cool/ui` keep the catalog's 19.3.0; mobile does not depend on `@trails-cool/ui`, so the split does not produce clashing React types. This clears the last `expo install --check` finding. - **`@sentry/react-native` deliberately left on 8.x.** SDK 57's bundled list names `~7.11.0` — a *major downgrade*. The package is already in `expo.install.exclude` for exactly this reason. Don't let `expo install --fix` "correct" it. ### Unmanaged native deps, checked against RN 0.86 before starting | Package | Peer | OK | |---|---|---| | `@maplibre/maplibre-react-native` | `react-native >=0.80.0` | ✅ | | `@sentry/react-native` 8.26.0 | `react-native >=0.65.0` | ✅ | | `@gorhom/bottom-sheet` | `reanimated >=4.0.0-` (57 ships 4.5.1) | ✅ | `expo-router/unstable-native-tabs` was the main risk, being an unstable API across a major. It still ships in 57, and `NativeTabs.Trigger` still exposes `.Label`/`.Icon`/`.Badge` with the same `sf`/`md` `{default,selected}` shape, so `app/(tabs)/_layout.tsx` is unchanged. `android/` and `ios/` are gitignored (CNG), so there is no hand-edited native code to reconcile. ### Verification | Check | Result | |---|---| | `expo install --check` | **Dependencies are up to date** (was 9 outdated) | | `pnpm typecheck` / `lint` / `test` | 14/14 · 14/14 · 12/12 | | `expo prebuild --clean` | all config plugins regenerate | | `gradle assembleDebug` | SUCCESSFUL — 758 tasks, 4 ABIs | | Install over SDK 56 build | in-place upgrade, no uninstall | | App boot on RN 0.86 | boots, session intact | | `NativeTabs` layout | all 4 tabs + icons render | | **MapLibre map (#148)** | **renders full-bleed on RN 0.86** | Verified on an Android emulator (Pixel 6 Pro, API 34) after rebasing onto `main` so the map from #148 was present — the SDK upgrade is confirmed against the new map screen, not just against the old placeholder. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(mobile): upgrade to Expo SDK 57 / React Native 0.86.3
Some checks failed
CD Staging / Build & Push Docker Images (pull_request) Has been skipped
CD Staging / Build & Push Docker Images-1 (pull_request) Has been skipped
CD Staging / Deploy Staging (pull_request) Has been skipped
CD Staging / Deploy PR Preview (pull_request) Has been skipped
CD Staging / Tear Down PR Preview (pull_request) Has been skipped
CI / Security Scan (pull_request) Successful in 48s
CI / Dockerfile Package Check (pull_request) Successful in 18s
CI / Visual Tests (pull_request) Successful in 1m19s
CI / Journal Image Smoke Test (pull_request) Successful in 11m23s
CI / E2E Tests (pull_request) Has been cancelled
CI / Checks (pull_request) Has been cancelled
a030d5169d
Moves apps/mobile from SDK 56 to SDK 57: expo ~56.0.12 -> ~57.0.22,
react-native 0.85.3 -> 0.86.3, expo-router ~56.2.19 -> ~57.0.21,
react-native-reanimated 4.3 -> 4.5.1, react-native-worklets 0.8.3 ->
0.10.1, plus every expo-* package. 25 specs in total, all taken from
SDK 57's own bundledNativeModules.json rather than picked by hand.

react stays on the catalog's 19.2.3, which is exactly what SDK 57 pins,
so no dependency change was needed there.

@types/react is pinned to ~19.2.4 for the mobile app only. Unlike react
it is not in pnpm-workspace.yaml's overrides block, so a per-app pin
takes effect; journal, planner and @trails-cool/ui keep the catalog's
19.3.0. Mobile does not depend on @trails-cool/ui, so the split does not
produce clashing React types. This clears the last `expo install
--check` finding: it now reports "Dependencies are up to date".

@sentry/react-native deliberately stays on 8.x. SDK 57's bundled list
names ~7.11.0, which would be a major downgrade; the package is already
in expo.install.exclude for this reason, and 8.26.0 declares
react-native >=0.65.0.

The other unmanaged native dependencies were checked against RN 0.86
before starting: @maplibre/maplibre-react-native (peer >=0.80.0) and
@gorhom/bottom-sheet (needs reanimated >=4.0.0-, satisfied by 4.5.1).

Verified: expo install --check clean; typecheck 14/14, test 12/12, lint
14/14; expo prebuild --clean regenerates Android with every config
plugin; gradle assembleDebug succeeds across all four ABIs; the APK
installs over the SDK 56 build in place and boots on an emulator with
the NativeTabs layout rendering correctly.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
fix(mobile): adopt the UIKit scene lifecycle so iOS 27 can launch
All checks were successful
CD Staging / Build & Push Docker Images (pull_request) Has been skipped
CD Staging / Build & Push Docker Images-1 (pull_request) Has been skipped
CD Staging / Deploy Staging (pull_request) Has been skipped
CD Staging / Deploy PR Preview (pull_request) Has been skipped
CD Staging / Tear Down PR Preview (pull_request) Has been skipped
CI / Security Scan (pull_request) Successful in 44s
CI / Dockerfile Package Check (pull_request) Successful in 17s
CI / Visual Tests (pull_request) Successful in 1m15s
CI / Journal Image Smoke Test (pull_request) Successful in 11m28s
CI / Checks (pull_request) Successful in 29m25s
CI / E2E Tests (pull_request) Successful in 5m12s
28282be3c1
Apps linked against the iOS 27 SDK are killed at launch unless they adopt
UIScene (Apple TN3187). Expo's prebuild template still emits an
AppDelegate/window lifecycle with no UIApplicationSceneManifest, so a
freshly prebuilt app dies before any JS runs:

    Application failed to launch: UIScene life cycle is required for apps
    built with this SDK.

This is not specific to SDK 57 — SDK 56 fails identically. Expo tracks it
in expo/expo#46664 (accepted, unreleased) and the fix is tied to React
Native 0.87+, so it lands no earlier than SDK 58.

Adds a config plugin that injects UIApplicationSceneManifest into
Info.plist and patches the generated AppDelegate.swift with a
SceneDelegate. It is taken from narrowstacks/dorkroom#205, itself derived
from YesterdaysLemon/expo-ios-scene-lifecycle-plugin. Beyond the minimum
manifest it forwards deep links, universal links and quick actions to the
Expo app-delegate subscribers, and re-dispatches the scene activation
callbacks, because UIKit stops calling the UIApplicationDelegate
life-cycle methods that Expo modules still rely on. Deep-link forwarding
matters here specifically: OAuth sign-in returns through
trailscool://auth/callback.

Two adjustments were needed for this repo:

- @expo/config-plugins becomes a direct devDependency. pnpm's isolated
  linker does not hoist it, so a local plugin cannot resolve it (the
  upstream PR uses bun, where it happens to be hoisted).
- eslint gets an override for apps/mobile/plugins. Config plugins run in
  Node during prebuild and must be CommonJS, which the repo's ESM rules
  otherwise reject.

Every anchor in the AppDelegate patch throws when it stops matching, so a
future template change fails prebuild loudly rather than silently
producing a non-scene, crashing app. Remove the plugin once Expo ships a
scene-based template.

Verified on an iOS 27 simulator (iPhone 17 Pro, Xcode 27): prebuild
injects the manifest and SceneDelegate, the app builds, launches, loads
2492 modules from Metro and renders — where the same build without the
plugin was killed at launch.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ullrich force-pushed upgrade-expo-sdk-57 from 28282be3c1
All checks were successful
CD Staging / Build & Push Docker Images (pull_request) Has been skipped
CD Staging / Build & Push Docker Images-1 (pull_request) Has been skipped
CD Staging / Deploy Staging (pull_request) Has been skipped
CD Staging / Deploy PR Preview (pull_request) Has been skipped
CD Staging / Tear Down PR Preview (pull_request) Has been skipped
CI / Security Scan (pull_request) Successful in 44s
CI / Dockerfile Package Check (pull_request) Successful in 17s
CI / Visual Tests (pull_request) Successful in 1m15s
CI / Journal Image Smoke Test (pull_request) Successful in 11m28s
CI / Checks (pull_request) Successful in 29m25s
CI / E2E Tests (pull_request) Successful in 5m12s
to ce1a8659f3
All checks were successful
CD Staging / Build & Push Docker Images (pull_request) Has been skipped
CD Staging / Build & Push Docker Images-1 (pull_request) Has been skipped
CD Staging / Deploy Staging (pull_request) Has been skipped
CD Staging / Deploy PR Preview (pull_request) Has been skipped
CI / Security Scan (pull_request) Successful in 1m41s
CI / Dockerfile Package Check (pull_request) Successful in 17s
CI / Visual Tests (pull_request) Successful in 1m21s
CI / Journal Image Smoke Test (pull_request) Successful in 2m46s
CI / Checks (pull_request) Successful in 28m15s
CI / E2E Tests (pull_request) Successful in 3m52s
Cancel superseded CI / Cancel in-flight CI (pull_request) Successful in 19s
CD Staging / Tear Down PR Preview (pull_request) Successful in 29s
2026-09-12 09:43:00 +00:00
Compare
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
trails-cool/trails!150
No description provided.