My Descent into the Podfile Abyss (and How I Climbed Out) Upgrading Expo SDK 50 → 51 in the Bare workflow to meet Apple’s iOS 18 SDK requirement.

If you’re a React Native developer you want to stay in the sweet spot of library stability—but sometimes a looming deadline forces your hand. You start getting emails like this:

App Store Connect Warning

Hello,

We noticed one or more issues with a recent delivery for the following app:

Quiznect

Version 1.0.40

Build 40

ITMS-90725: SDK version issue - This app was built with the iOS 17.5 SDK. Starting April 24, 2025, all iOS and iPadOS apps must be built with the iOS 18 SDK or later, included in Xcode 16 or later, in order to be uploaded to App Store Connect or submitted for distribution.

Apple’s deadline arrived, so I had to upgrade my Expo app. I ran:

npx expo upgrade

…and hoped for the best. Sometimes it works flawlessly. Other times you end up on a multi‑day debugging odyssey through Podfiles, build logs, environment variables, and EAS configurations.

This is the story of my upgrade from Expo SDK 50 → SDK 51 in the Bare Workflow. It wasn’t pretty, but I emerged with an iOS 18 SDK‑compliant build. May my tale of woe and triumph save you some time and sanity!

The Goal
• Upgrade from Expo SDK 50 to SDK 51
• Meet Apple’s iOS 18 SDK requirement (Xcode 16+) by April 24, 2025
• Stay reasonably up‑to‑date without jumping straight to SDK 52

Initial Steps & The First Hurdle: Dependencies & Workflow

  1. Commit all changes, then run: npx expo upgrade
  2. Expo CLI pointed out manual steps—because I use the Bare workflow (I manage ios/ & android/ in Git).

Lesson 1: Know your workflow (Managed vs. Bare). The native‑dir upgrades differ significantly. I had an old patch-package workaround causing build failures on the build server.

Lesson 2: Clean up obsolete workarounds & devDependencies before upgrading.

Targeting the Right SDK (and Dependencies)

I first tried expo@latest (SDK 52) but pivoted to the stable SDK 51. In package.json:

"dependencies": {
"expo": "~51.0.0", // target SDK 51
"react": "...",
"react-native": "...",
// …
},

Then:

npx expo install --fix

npm list expo react react-native expo-modules-core

When mismatches persisted, I:

npm cache clean --force
sudo rm -rf node_modules
rm package-lock.json
npx expo install --fix

Verified correct versions:
• Expo 51.x
• React 18.2.0
• RN 0.74.x
• expo‑modules‑core 1.12.x

Lesson 3: Manually set your SDK version in package.json
• Clean‑install when deps get stuck
• Always verify with npm list, don’t blindly trust --fix

The Pod Install Gauntlet

cd ios
npx pod-install

Common Errors & Fixes

  1. uninitialized constant FlipperConfiguration - Remove or comment out old Flipper logic in ios/Podfile.
  2. cannot load such file ... native_modules - Comment out the bad require_relative in Podfile or install @react-native-community/cli.
  3. undefined method use_expo_modules! - Ensure require for expo/scripts/autolinking is present.
  4. React-Runtime Hermes → minimum deployment target; Bump platform :ios, '15.0' (RN 0.74 needs ≥ 14.0/15.0).
  5. Unable to find a specification for fmt: rm ios/Podfile.lock to clear stale lock.
  6. Unable to find a specification for ExpoModulesCore: npm uninstall expo-random → npx expo install expo-crypto npx expo install expo-modules-core pod cache clean --all + pod repo update Ensure source: 'https://cdn.cocoapods.org/' in Podfile.

Lesson 4: Pod errors come in waves—fix one, the next appears.

Lesson 5: Match your iOS deployment target to your RN version.

Lesson 6: Deleting Podfile.lock often forces a fresh, clean resolution.

Lesson 7: Migrate deprecated packages early.

Lesson 8: Verify JS 👉 Native (.podspec) alignment.

The EAS Build Environment Saga

Cloud builds have their own quirks:

eas build --profile production --platform ios

• expo doctor failures → fix locally, commit lockfiles.
• eas.json rejected cocoapodsVersion → remove it or update eas-cli.
• module redefinition (ReactCommon) → selective :modular_headers => true in Podfile.
• Apple required Xcode 16 → set in eas.json:

// eas.json excerpt
{
"build": {
"production": {
"ios": {
"image": "macos-sonoma-14.6-xcode-16.1"
}
}
}
}

Lesson 9: Always fix expo doctor issues locally and commit everything.

Lesson 10: Keep your eas-cli up‑to‑date and resolve PATH conflicts.

Lesson 11: If eas.json keys fail, check for deprecations or CLI versions.

Lesson 12: Complex native errors sometimes need granular CocoaPods tweaks.

Success!

Final command:

eas build --profile production --platform ios --clear-cache

✅ Result: A successful Expo SDK 51 build against Xcode 16/iOS 18 SDK.

Upgrading in the Bare workflow isn’t always straightforward, but with methodical troubleshooting—both on the JS side and in CocoaPods & EAS—you can emerge victorious. Happy upgrading!