iOS Platform Best Practices
A condensed summary of 25 iOS platform essentials for Expo SDK 57 teams - parity decisions when Android can do more cheaply, drawn from every page in this section.
Search across all documentation pages
A condensed summary of 25 iOS platform essentials for Expo SDK 57 teams - parity decisions when Android can do more cheaply, drawn from every page in this section.
Open .xcworkspace, not .xcodeproj: CocoaPods projects fail mysteriously when the workspace is skipped - open ios/*.xcworkspace is the only correct Xcode entry when Pods/ exists.
CLI-first daily builds: Prefer npx expo run:ios over living in Xcode - native IDE is for signing mysteries, Instruments, and pod compile errors, not JS iteration.
Simulator for speed, device for truth: Push, universal links, Live Activities, BGTaskScheduler, camera, and biometrics require physical hardware - simulators give false confidence.
Gitignore ios/ under CNG: Commit app.config.ts and plugins; regenerate native on EAS or npx expo prebuild - hand-edited pbxproj drifts on every SDK bump.
Commit Podfile.lock when ios/ is in git: Bare teams need deterministic pods on CI - pod install not pod update on release branches.
Use npx pod-install from repo root: Avoids wrong-directory Pods/ and matches Expo's expected CocoaPods invocation across M-series and Intel Macs.
Prefer arm64 simulators on Apple Silicon: Rosetta x86_64 simulators are slower and cause architecture mismatch linker errors with modern RN 0.86 pods.
Entitlements ship in the IPA, not OTA: Push, associated domains, app groups, and background modes need eas build after app.config.ts changes - EAS Update cannot add capabilities.
Declare associatedDomains with applinks: prefix: Universal links fail silently in Safari when the prefix is wrong - pair with hosted AASA and rebuild; see iOS Universal Links.
Match Apple Developer portal and config: App ID capabilities, bundle identifier, AASA appID, and EAS credentials must be identical strings - typos open Safari instead of your app.
Production push uses production APNs: TestFlight and App Store builds use aps-environment: production - sandbox tokens never deliver to TestFlight users.
Only declare UIBackgroundModes you implement: Speculative audio, location, or fetch entries trigger Guideline 2.5.4 - remove copy-paste modes from infoPlist.
Run npx expo-doctor before every iOS release: Catches version skew, invalid config, and dependency mistakes before they burn a TestFlight cycle.
Aggregate privacy manifests before upload: Merge NSPrivacyAccessedAPITypes from Expo SDK and third-party PrivacyInfo.xcprivacy files - ITMS-91053 blocks processing in minutes.
Align Privacy Nutrition Labels with runtime SDKs: Crash reporters, analytics, and auth SDKs collect data - labels must match manifests and Guideline 5.1.1 or human review rejects.
Offer Sign in with Apple when offering Google/Facebook account creation: Guideline 4.8 is enforced - expo-apple-authentication is cheaper than resubmission loops.
Ship in-app account deletion: If users can register in-app, Settings → Delete Account must call your backend - web-only deletion links fail review in 2026.
Test Release builds before eas submit: Metro Debug hides missing native modules, bad env embed, and Hermes production-only crashes - dogfood production or preview profiles.
Use TestFlight internal for engineering, external for UAT: Internal skips Beta App Review (100 Connect users); external scales to 10,000 with a lighter Apple gate - both expire at 90 days.
Plan TestFlight rebuild cadence: Long betas need scheduled eas build before expiration - automate Connect API reminders at 14 days remaining.
Fill App Review Information with working demo credentials: Reviewers cannot log in is an instant 2.1 rejection - rotate passwords and document guest paths in notes.
Widgets and Live Activities are separate targets: expo-widgets uses SwiftUI - not RN views; requires app groups, dev client, and meaningful utility to pass 4.2 - not Expo Go.
Do not cargo-cult Android patterns on iOS: Side-loading APKs, arbitrary background services, and intent-based deep links have no iOS equivalent - invest in entitlements, AASA, and TestFlight instead.
Verify entitlements in the built IPA before blaming JS: codesign -d --entitlements :- on the archived .app settles associated-domain disputes faster than guessing Router config.
Document iOS-only scope in parity ADRs: When Android ships widgets or flexible backgrounds first, record iOS timeline, review risk, and user impact - stakeholders should not assume feature symmetry without native cost.
eas build --local reproduces cloud when needed.expo install --fix + prebuild.ios/ only when brownfield merges or enterprise policies require it - see Prebuild Basics.ios/ absent), pod install, compile, and sign - your app.config.ts is still the source of entitlements and manifests.Stack versions: This page was written for React 19.2.3, React Native 0.86.0, and Expo SDK 57 (
expo~57.0.4).
Reviewed by Chris St. John·Last updated Jul 19, 2026