Skip to content

Upgrade mobile to Expo SDK 56#5886

Draft
janicduplessis wants to merge 10 commits into
developfrom
janic/expo-56
Draft

Upgrade mobile to Expo SDK 56#5886
janicduplessis wants to merge 10 commits into
developfrom
janic/expo-56

Conversation

@janicduplessis

Copy link
Copy Markdown
Contributor

Summary

Upgrades the mobile app from Expo SDK 54 to SDK 56 (React Native 0.81 → 0.85, React 19.1 → 19.2). Both iOS and Android build and run on the new architecture with prebuilt React Native — no buildReactNativeFromSource.

Changes

  • Dependencies: bump expo to ~56.0.5 and the SDK-bundled expo-* packages, react-native 0.85.3, react 19.2, @react-native-firebase/* 24, Skia, FlashList, reanimated/worklets, etc. Root pnpm.overrides pin the SDK 56 versions to keep the workspace's * peer deps from dragging in SDK-era transitives.
  • Android build system: SDK 56 removed useExpoModules() / scripts/autolinking.gradle, so settings.gradle moves to the expo-autolinking-settings plugin + expoAutolinking.useExpoModules()/useExpoVersionCatalog(). app/build.gradle renames the reactAndroidLibs catalog to expoLibs and enables core library desugaring (required by recaptcha-enterprise). MainApplication.kt migrates to ExpoReactHostFactory.getDefaultReactHost.
  • iOS build system: AppDelegate.swift migrates to the SDK 56 API (drops the React_RCTAppDelegate import — those symbols come through React now — uses internal import Expo, removes the deleted bindReactNativeFactory). RNFB is kept out of the prebuilt-React framework-module conflict via ios.forceStaticLinking, and Error+logWithDomain.swift uses the direct FirebaseCrashlytics API since static-linked RNFB exposes no Swift module.
  • expo-av → expo-audio: voice-memo recording/playback migrated to the new hooks API; expo-av removed.
  • Patches: dropped stale SDK-54-era patches (react-native@0.81.5, reanimated, screens, expo-share-intent, expo-image-picker, etc.); bumped @gorhom/bottom-sheet to 5.2.14 (the unstable_getBoundingClientRect crash fix is upstream now, so the patch is slimmed to just the flex-overflow fix); added a tentap patch for RN 0.85's removal of UIManagerModule.onBatchComplete().

How did I test?

  • iOS: expo run:ios on an iPhone 17 simulator (Xcode-signed build). App boots, connects to Metro, renders the onboarding/welcome flow; sign-up and login paths exercised. Built with prebuilt React Native (RCT_USE_PREBUILT_RNCORE is the SDK 56 default).
  • Android: expo run:android / gradle assembleProductionDebug on an API 35 emulator. Debug APK builds and installs; navigated welcome → sign-up invite screen and back.
  • expo-doctor clean; pnpm -r tsc passes; pnpm install is green with all patches applying.
  • Devices were fresh installs (not logged into a ship), so testing covered the onboarding surface and app boot, not authenticated in-app navigation. Worth a deeper logged-in pass before this leaves draft.

Risks and impact

  • Safe to rollback without consulting PR author? Yes — revert the PR to return to SDK 54, but it requires pnpm install + a clean native rebuild (pods / gradle) afterward since native folders and the lockfile change.

  • Affects important code area:

    • Onboarding
    • State / providers
    • Message sync
    • Channel display
    • Notifications
    • Other: native build system (iOS Podfile/AppDelegate, Android gradle/MainApplication), app startup, dependency tree

    (Notifications: AppDelegate.swift remote-notification forwarding and MainApplication.kt notification-channel setup are in the touched startup files; the handler logic itself is unchanged.)

Rollback plan

Revert the PR's commits (or revert the merge), then run pnpm install, pod install, and a clean gradle build. No data migrations or server-side changes are involved, so rollback is purely client-side.

Screenshots / videos

Onboarding/welcome and sign-up screens render correctly on both iOS (iPhone 17 sim) and Android (API 35 emulator). Can attach captures on request.

- expo ~56.0.5 (from ~54.0.33), react-native 0.85.3, react 19.2.3
- All expo-* packages bumped to 56.x
- react-native-reanimated 4.3.1, react-native-worklets 0.8.3
- @shopify/react-native-skia 2.6.2, @shopify/flash-list 2.0.2
- @sentry/react-native ~7.11.0, @react-native-community/netinfo 12.0.1
- pnpm.overrides updated (react 19.2.3, reanimated 4.3.1,
  gesture-handler ~2.31.1); removed dead expo-audio override

Patches audited:
- DROPPED (now upstream in SDK 56): react-native@0.81.5,
  expo-share-intent@5.1.1, expo-image-picker@17.0.10, expo-task-manager@14.0.9
- DROPPED (dead/unused): @10play/tentap-editor@0.4.55, @tiptap/react@2.0.3,
  @urbit/http-api@3.1.0-dev-3, usehooks-ts@2.6.0
- DROPPED (orphan files): expo-image-picker@16.0.6, expo-share-intent@3.2.3,
  react-native-screens@4.4.0
- DROPPED (architecture rewrite in SDK 56, team to re-implement if needed):
  react-native-reanimated@4.1.6 (web-compat null-safety),
  expo-background-task@1.0.10 (BGAppRefresh-vs-BGProcessing rewrite required
  against new actor-based scheduler)
- REGENERATED for new version: react-native-gesture-handler@2.28.0 → 2.31.2
  (Swipeable pointerEvents Android fix)
- KEPT unchanged (versions still match): tentap-editor 0.5.21,
  bottom-sheet 5.2.6, audio-waveform 2.1.6, tamagui rc.41, jest-dom types,
  any-ascii, drizzle-orm, react-cosmos, country-codes-picker, tailwind-rn

pnpm install runs clean with all 13 remaining patches applying.
Wildcard peer deps in packages/app and packages/ui were causing pnpm
to resolve old SDK 54-era versions of expo-* and other native modules
alongside the SDK 56 versions in the mobile app. The conflicting graph
prevented @expo/cli from loading @expo/env@2.3.0 (it was nesting an
older 2.0.8 from a stale transitive expo-constants@18).

Pin react-native, @types/react, and all duplicated expo-* / native
modules to single versions so the install produces one snapshot.
Remove the now-stale direct dep on expo-dev-menu — expo-dev-client
pulls it in transitively at 56.0.15.
- expo-media-library no longer re-exports PermissionStatus; import it
  directly from expo-modules-core instead
- Animated.View style arrays in InteractableChatListItem don't satisfy
  Reanimated 4's stricter style typing when mixed with object literals.
  Extract static styles via StyleSheet.create so the inferred type
  collapses to ViewStyle.
expo-av's iOS module imports ExpoModulesCore/EXEventEmitter.h, which is
no longer shipped with SDK 56. Replace the two usages with expo-audio:
- AudioPlayer for playback (useAudioPlayer + useAudioPlayerStatus)
- MicInput for recording (useAudioRecorder + AudioModule)

Also bump packages whose SDK 54-era versions don't build against the
SDK 56 native module surface:
- @google-cloud/recaptcha-enterprise-react-native 18.3 -> 18.8.2
  (uses install_modules_dependencies instead of the obsolete RCT-Folly
  dep)
- @react-native-firebase/* 22 -> 24 (Firebase SDK 12)
- expo-speech-recognition 3.1.2 -> 56.0.0 (new Promise.ResolveClosure)

Drop expo-av peer dep from packages/ui.
- Raise iOS deployment target to 16.4 (required by SDK 56 Expo
  modules) in both Podfile.properties.json and the Landscape xcodeproj
- Replace Swift import of RNFBCrashlytics with FirebaseCrashlytics
  direct usage in Error+logWithDomain.swift, so the file no longer
  depends on @react-native-firebase exposing a Swift module
- Add a post_install hook for RNFB* pods that allows non-modular
  React-Core includes and suppresses the modern clang -Wimplicit-int
  error in @react-native-firebase's RCT_EXPORT_METHOD macros
settings.gradle uses the expo-autolinking-settings plugin with
expoAutolinking.useExpoModules() / useExpoVersionCatalog(); SDK 56
removed the old scripts/autolinking.gradle and useExpoModules().
app/build.gradle renames the reactAndroidLibs version catalog to
expoLibs and enables core library desugaring (required by
recaptcha-enterprise). MainApplication.kt moves to
ExpoReactHostFactory.getDefaultReactHost and sets the new-arch
release level. gradle.lockfile regenerated for the new versions.
AppDelegate.swift drops the React_RCTAppDelegate import (those symbols
come through React now), uses `internal import Expo`, and removes the
removed bindReactNativeFactory call. NotificationLogProcessor.swift
uses `internal import ExpoModulesCore`. Podfile.properties.json adds
ios.forceStaticLinking for the RNFB pods so they build as static libs
outside the framework-module system, avoiding non-modular-include
errors against the prebuilt React-Core headers; the corresponding
RNFB clang post_install workaround is removed from the Podfile.
The unstable_getBoundingClientRect crash fix (the typeof guard) landed
upstream in 5.2.10, so the patch is now slimmed to carry only the
flex-overflow fix for TLON-5660.
UIManagerModule.onBatchComplete() was removed in RN 0.85, so
TenTapViewManager.dispatchUIUpdate no longer compiles. Drop the manual
UI-manager flush and run the update directly on the UI thread; Fabric
dispatches view ops eagerly so the explicit flush is unnecessary.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant