Guideline 2.5.1: Public APIs and Up-to-Date SDKs with Expo
By Mehdi Azizi
Published , 9 min
Guideline 2.5.1: public APIs only, an app that runs on the current iOS, frameworks used as intended. With Expo, check your native modules first.
Guideline 2.5.1 sets four requirements: use only public APIs, run on the current version of iOS, phase out whatever Apple deprecates, and use each framework for its intended purpose. With Expo, the risk rarely comes from your JavaScript code. It comes from the native modules you install and the SDK you build with.
I publish Solys, TradaZen and Nolera with Expo SDK 57. Solys relies on Apple's Screen Time API, a framework with restricted use. This guide covers the review rule. For the error you get when uploading the build, read the ITMS-90338 guide. For background modes, which sit next to it in the same section, read the one on guideline 2.5.4.
What does guideline 2.5.1 say?
The rule lives in section 2.5, "Software Requirements," of the App Review Guidelines: "Apps may only use public APIs and must run on the currently shipping OS. Keep your apps up-to-date and make sure you phase out any deprecated features, frameworks or technologies that will no longer be supported in future versions of an OS. Apps should use APIs and frameworks for their intended purposes and indicate that integration in their app description."
In plain terms, four obligations:
- Public APIs only, meaning APIs documented by Apple.
- An app that runs on the current version of iOS, which has been iOS 27 since September 2026.
- Nothing deprecated that's about to disappear: you replace whatever Apple announces as end of life.
- Every framework in its place, and mentioned in the description: HomeKit for the smart home, HealthKit for health and fitness, connected to the Health app.
Hence the rejection title: "Guideline 2.5.1 - Performance - Software Requirements".
Public APIs: where the risk hides in an Expo app
A private API is a system function Apple doesn't document. Apple guarantees neither its behavior nor that it will still exist in the next iOS version, which is why it's banned.
Your JavaScript code never touches iOS directly: it goes through native modules. The ones in the Expo SDK are maintained by the Expo team. The others ship code you didn't write, and Apple reviews it just like your own. The ITMS-90683 guide shows the same mechanism with permission strings: Apple judges the code present in the build, not what the app does.
On Solys, as of October 15, 2026, 42 direct dependencies contain native iOS code, 14 of them outside the Expo SDK. Among them: RevenueCat, Sentry, AppsFlyer and react-native-device-activity, the bridge to the Screen Time API.
A private API can be caught at two points. On upload, App Store Connect's automated scan can return error ITMS-90338. During review, the reviewer cites 2.5.1.
Deprecated APIs: from UIWebView to UIScene
The best-known example is UIWebView, the old component for displaying web content. Apple stopped accepting it in new apps in April 2020, then in updates in December 2020. An old library that still referenced it blocked the upload. On Solys, the search from the checklist below finds no references.
The next change is already announced. Apple's technical note TN3187 says it: "In the next major release following iOS 26, UIScene lifecycle will be required when building with the latest SDK; otherwise, your app won't launch." In plain terms, an app built with the iOS 27 SDK without the scene-based life cycle won't launch.
Three dates to connect:
- Since April 2026, Apple has required Xcode 26: that's the topic of the ITMS-90725 guide.
- In April 2027, according to Apple's submission page, the iOS 27 SDK becomes mandatory.
- Expo SDK 58, in beta since September 15, 2026, already generates scene handling, according to Expo's announcement.
With Expo SDK 57 and Xcode 26, you're still building with the iOS 26 SDK: nothing breaks today. Modules that modify the AppDelegate will have to catch up before April 2027.
Frameworks used outside their intended purpose
The reviewer checks that a sensitive framework powers a visible feature, and that your listing mentions it.
- HealthKit is for health and fitness. Apple's interface guidelines say it: "If your app doesn't provide health and fitness functionality, don't request access to people's private health data."
- HomeKit is for the smart home, and nothing else.
- PushKit is for voice over IP calls. The PushKit documentation is clear: "If you are unable to support CallKit in your app, you cannot use PushKit to handle push notifications." A VoIP notification that wakes the app without a call is misuse.
- Family Controls, the Screen Time API, requires a special entitlement. Its documentation states: "Before submitting your app to the App Store, you must request permission to use the entitlement."
For Solys, I requested this distribution entitlement on September 23, 2026, and Apple granted it the same day. The use matches the framework: the user picks the apps to pause, and Solys blocks them for 30, 45 or 60 minutes after waking up. The listing description says it in one sentence: "Solys uses Apple's Screen Time authorization to pause the selected apps." That's what the end of 2.5.1 asks for: mention the integration in the description.
Beta versions: TestFlight yes, App Store no
The App Store Connect release notes show the rule in practice. On October 14, 2026, builds made with Xcode 27.2 beta 3 are accepted "for internal and external testing," so for TestFlight. On October 5, those from Xcode 27.1 RC are accepted "for the App Store."
A build compiled with an Xcode beta goes to TestFlight, never to review. To submit, you need a final release, or an RC that Apple has opened to the App Store.
Two clarifications for an Expo app:
- An Expo beta isn't an Apple beta. Apple checks the Xcode and iOS SDK version, not the Expo version. The SDK 58 announcement added that EAS images with Xcode 27 were coming soon.
- Testing on the current iOS is still mandatory, even if you build with the iOS 26 SDK. Cold-launch the app on an up-to-date iPhone or simulator.
Checklist: native dependencies before submission
Run through it before every production build. Run the commands at the root of the Expo project:
npx expo-doctor
npx expo install --check
grep -rlw "UIWebView" node_modules --include='*.m' --include='*.mm' --include='*.swift' --include='*.h'
npx expo-doctorreports no failures. On Solys, on October 15, 2026: 20 of 21 checks passed. The failure came from 13 Expo packages one patch behind, whichnpx expo install --checkaligns.- Every module outside the Expo SDK is listed in React Native Directory. Expo Doctor runs this check by default, as the Expo documentation states. An unknown package deserves a read of its iOS code.
- Every native module keeps up with your React Native version. The Expo versions table pairs SDK 57 with React Native 0.86. A module with no release for several versions is a warning sign.
- The UIWebView search returns nothing. Do the same for any API Apple announces as end of life.
- Every sensitive framework has its visible feature: health, Screen Time, calls, home. The description mentions it, and the special entitlement is granted.
- The build comes from a final Xcode. Check the version in the EAS build logs.
- The app cold-launches on the current version of iOS, with a fresh account.
- Modules that touch the AppDelegate announce UIScene support before you move to the iOS 27 SDK.
How do you respond to a 2.5.1 rejection?
The message usually names the API or framework in question. Two cases:
- The framework powers nothing visible: remove the module or capability, make a new build, then reply in two sentences.
- The framework powers a real feature: give the exact path, a screenshot or a short video, and mention the integration in the listing description.
Removing a native module changes the binary: an over-the-air update isn't enough. The guideline 2.1 guide explains why the reviewer only sees the bundled code. The conversation then happens in App Store Connect, as in the guide on replying to App Review.
Check your project before uploading
The Review Kit bundles my pre-upload checklist, my fixed rejections and Claude Code skills that review your Expo project. If you want a human eye, book an hour with me: we'll go through Apple's message and your dependencies together. The full approach is on the app rejected by Apple page.
Getting ready for your first release? Take the free diagnostic: it sends you my Apple anti-rejection checklist.
Frequently asked questions
What does "Guideline 2.5.1 - Performance - Software Requirements" mean?
It's the rejection title when Apple believes your app uses a non-public API, a deprecated technology or a framework outside its intended purpose. The message usually names the element in question.
What's the difference between guideline 2.5.1 and error ITMS-90338?
ITMS-90338 is an automated error you get when uploading the build, when the scan detects a non-public symbol. 2.5.1 is the review rule applied by a person, and it also covers misused frameworks and deprecated technologies.
Can you submit an app built with an Xcode beta?
No. App Store Connect accepts those builds for TestFlight only. For review, build with a final version of Xcode, or an RC that Apple has opened to the App Store in its release notes.
Can an Expo app use a private API without knowing it?
Yes, through a third-party native module. Your JavaScript code doesn't call iOS itself, but every native package ships Objective-C or Swift code. Go through the list of modules outside the Expo SDK before every release.
Should you move to Expo SDK 58 right away?
Not for 2.5.1 as of October 15, 2026: SDK 57 builds with the iOS 26 SDK, which is still accepted. Plan the upgrade before April 2027, when Apple will require the iOS 27 SDK and the scene-based life cycle.

