Expo EAS iOS Privacy Manifest Fixes (2026)

Yatish Goel

Yatish Goel

Co-Founder & CTO

February 14, 2026
#expo#eas build#ios#privacy manifest#PrivacyInfo.xcprivacy#xcode 16#react native

Expo EAS iOS Privacy Manifest Fixes (2026)

If you ship an Expo app to iOS in 2026, you are probably living in EAS Build and you are probably tired.

One weird class of failures keeps showing up when teams jump SDK versions or flip between managed and bare: PrivacyInfo.xcprivacy.

Sometimes it is an App Store email problem. Sometimes it is a build-time Xcode problem. Sometimes it is both. The worst part is that the error text points you at the wrong fix.

This post is a field guide to the failures we keep seeing on client projects, what they mean, and how to get back to green builds without doing a week-long yak shave.

I am going to be opinionated: treat privacy manifests like any other native artifact. If you let it drift, it will bite you.

The 2026 reality check: Apple keeps moving the goalposts

Apple has been tightening iOS submission rules. In 2025, Apple started rejecting uploads that were not built with Xcode 16 and the iOS 18 SDK. Expo called this out plainly: as of April 24, 2025, uploads to App Store Connect must be built with Xcode 16 or later using the iOS 18 SDK.

That change is still in effect in 2026. If you are on an older build image, you can be “fine locally” and still fail at upload.

So you end up upgrading Expo SDK or switching your EAS build image, and that is when privacy manifests start popping up.

What a PrivacyInfo.xcprivacy file actually is

A privacy manifest is a file named PrivacyInfo.xcprivacy included in your iOS app target. It declares why your app (or its native deps) calls certain “required reason” APIs.

Expo’s docs list examples like UserDefaults access, file timestamps, system boot time, disk space, and active keyboard. Apple treats the list as open-ended, which is a nice way of saying: expect more of these over time.

If you are using Expo’s config, you can generate the file through app config using expo.ios.privacyManifests and an NSPrivacyAccessedAPITypes array.

Expo also notes a nasty detail: Apple does not correctly parse all PrivacyInfo files included by static CocoaPods deps (including Expo packages). Translation: you may still need to copy reasons into your app’s own manifest even if your deps ship their own.

That sentence explains about 60% of the “but the package already has a privacy file” arguments we hear.

The failure modes we see in real projects

Most guides talk about “add the manifest”. Cool. That is not what breaks.

These are the failures that actually block teams.

Failure mode 1: “Multiple commands produce … PrivacyInfo.xcprivacy”

Typical error (from a real Expo SDK 51 upgrade issue):

“Multiple commands produce … MyAppName.app/PrivacyInfo.xcprivacy”

This is almost always you adding the privacy manifest in two places.

Common ways it happens:

• You added expo.ios.privacyManifests in app.json and you also committed a manual PrivacyInfo.xcprivacy file in ios/YourApp.

• You are in the middle of switching workflows. The native ios/ folder exists, but you also have config plugins, and prebuild tries to sync config into native. Now you have one file from config and one from your Xcode project.

How to fix it (pick one source of truth):

1) If you are managed or “prebuild-driven”, delete the manual PrivacyInfo.xcprivacy from ios and keep the app.json config.

2) If you are truly bare and you want to manage native yourself, remove the privacyManifests config and keep the manual file in Xcode.

Do not keep both.

Cost and time impact we see: 1-2 hours when the team knows what changed, 1-2 days when it is hidden inside a messy upgrade.

Failure mode 2: “EAS Build will not sync your native configuration”

Expo doctor calls this out bluntly when it sees ios or android folders present alongside config plugins.

If your ios folder is present, EAS Build treats it as “you own native now”. It will not apply prebuild changes for you.

That leads to this trap:

• You updated app.json with privacyManifests

• You pushed

• Cloud build still does not include the file

• Apple emails you about missing reasons later

Fix:

• Decide what workflow you are in.

• If you want prebuild behavior, gitignore ios and android and let EAS generate them.

• If you want bare behavior, stop expecting app.json to patch your native project. Edit Xcode instead.

This decision is boring, but it saves money.

Failure mode 3: “Apple still emails you even though your deps have PrivacyInfo files”

Expo’s own docs say Apple does not correctly parse all privacy files from static CocoaPods dependencies.

So you can be “correct” and still get the email.

What we do in practice:

• Build and submit to TestFlight external review.

• Wait for Apple’s email listing the missing reasons.

• Copy those reason codes into your app-level PrivacyInfo.xcprivacy (or app.json privacyManifests) and resubmit.

This feels backwards, but it is the fastest path when you are shipping and the list keeps changing.

Budget this as one extra upload cycle. On US-based teams, that is usually half a day plus waiting time.

Failure mode 4: You fixed privacy manifests, then App Store upload fails with ITMS-90725

This one is not a privacy issue, but it often shows up right after you fix privacy because you changed your build setup.

Expo’s “Apple SDK minimum requirements” post spells it out: Apple requires Xcode 16 and iOS 18 SDK for uploads.

If you are on Expo SDK 52 or later, EAS Build defaults to Xcode 16. If you are on SDK 51 or earlier, Expo strongly recommends upgrading, or you can explicitly set an iOS build image with Xcode 16 in eas.json.

Example snippet Expo shows:

• ios.image: macos-sequoia-15.3-xcode-16.2

So if you are stuck on SDK 51 for a client with legacy native code, you can still ship, but you must pick the right image.

Failure mode 5: The “SDK 50 to 51” upgrade that turns into a Podfile horror story

There is a solid Medium write-up from April 2025 about upgrading a bare Expo app from SDK 50 to 51. It is basically a bingo card of what we see:

• Flipper config issues

• Missing React Native CLI bits

• use_expo_modules! problems

• iOS deployment target bumps (going up to 15.0)

• Podfile.lock conflicts

• ExpoModulesCore resolution issues

• EAS environment differences versus local

None of those are “privacy manifest” problems, but the trigger is the same: you upgraded because Apple changed submission requirements.

Our rule: if you are doing a bare upgrade under deadline, do it in two passes.

Pass 1: get a local Debug build running in Xcode. No EAS yet.

Pass 2: get an EAS build running with the correct Xcode image.

If you jump straight to cloud builds, you pay in log scrolling.

A practical checklist that gets teams unstuck

Here is the checklist we run on iOS build failures that smell like privacy manifests.

1) Identify your workflow in one minute

Answer this:

• Is ios/ committed in git?

If yes, you are bare-ish. EAS will not sync config.

If no, you are prebuild-managed. EAS will generate ios.

Do not argue with the tooling. Decide and move on.

2) Search for duplicate manifests

Look for PrivacyInfo.xcprivacy in:

• ios/

• your Xcode target resources

• any config plugin output

If you have two, you will get the “Multiple commands produce” error.

3) If you use app.json privacyManifests, keep it small and explicit

Start with only what you know you need.

Example from Expo docs:

• NSPrivacyAccessedAPICategoryUserDefaults with reason code CA92.1

Add more reasons only when Apple tells you.

4) Treat Apple’s email as the source of truth

This is the part people hate.

But Apple’s upload pipeline is the actual enforcement point. Your local build passing does not prove the upload is compliant.

Plan for one TestFlight submission as part of the work.

5) Upgrade your EAS build image before you touch code

If your only goal is “stop App Store rejecting us”, set the Xcode 16 image first.

Then see what breaks.

If you are in the UK or EU and working with a distributed team, this saves you from the classic timezone loop where one person upgrades dependencies and another person changes build images, and then no one knows which change broke it.

6) Budget a real rescue window

If this is a founder project with one React Native dev, here is the honest budget we give:

• Simple managed app: 2-4 hours

• Bare app with native drift: 1-3 days

• Bare app with old pods and custom Swift/ObjC: 3-7 days

US agency rates vary, but at $120 to $200 per hour, that is the difference between a $600 fix and a $8,000 fire drill.

If someone promised you “just bump Expo SDK and ship”, they were guessing.

What to do if you are stuck on SDK 51 (or earlier) in 2026

Sometimes you cannot upgrade quickly. Maybe a vendor SDK is pinned. Maybe the app has custom native code and nobody wants to touch it.

You still have options:

• Keep SDK 51, but set an EAS iOS build image that includes Xcode 16 and iOS 18 SDK.

• Keep privacy reasons app-level, because Apple may miss the ones in static pod deps.

• Run expo doctor and believe it when it says your workflow is mixed.

This is not “best practice”. It is survival.

The stance: privacy manifests are not paperwork, they are build inputs

If you treat PrivacyInfo.xcprivacy like legal boilerplate, it becomes a last-minute scramble.

Treat it like any other native resource file:

• one source of truth

• committed, reviewed, and tested

• verified via a TestFlight submission

Your future self will thank you. Or at least, your future self will get a full night of sleep.

Sources

• Expo docs: Privacy manifests (updated May 1, 2025)

• Expo blog: Apple SDK minimum requirements (published April 24, 2025)

• GitHub issue: [expo-51] Build error with PrivacyInfo.xcprivacy after upgrading to SDK 51

• Medium: React Native Expo SDK 51 Upgrade: My Descent into the Podfile Abyss (and How I Climbed Out) (Apr 14, 2025)

• Expo docs: Troubleshoot build errors and crashes (updated Jan 4, 2026)

---

Related reading

Yatish Goel

Yatish Goel

Co-Founder & CTO

US Startup ExperienceIIT Kanpur

Full-stack architect with US startup experience and an IIT Kanpur degree. Yatish drives the technical vision at HeyDev, designing robust architectures and leading development across web, mobile, and AI projects.

Related Articles