Skip to content
Back To School Sale from $619, until 31st October 2026 Book now
appsubmitter.io
Apple App Store Guideline 2.5.1

App Store Guideline 2.5.1 Software Requirements: find the non-public or deprecated API and resubmit

Guideline 2.5.1 requires apps to use only public APIs, run on the currently shipping OS, phase out deprecated technology and use frameworks such as HealthKit or HomeKit for their intended purpose. The #1 cause is a third-party SDK or plugin that references a non-public or deprecated symbol. The fix: trace the flagged symbol to its binary with grep, nm or a link map, update or remove that dependency, build with Xcode 26 or later, and upload a new build with a short, specific reply.

By the appsubmitter.io App Specialist Team, updated , 12 min read

What the rejection typically looks like

Guideline 2.5.1 - Performance - Software Requirements

Your app uses or references the following non-public or deprecated APIs:

Frameworks/SomeSDK.framework/SomeSDK
Symbols: _IOPSCopyPowerSourcesInfo, _IOPSGetPowerSourceDescription

The use of non-public or deprecated APIs is not permitted on the App Store, as they can lead to a poor user experience should these APIs change. Continuing to use or conceal non-public or deprecated APIs in future submissions may result in the termination of your Apple Developer account.

Next Steps
If you use third-party libraries, update to the most recent version of those libraries. The strings, otool and nm tools can help you find where the code resides.

Paraphrased example – the exact wording in your message may differ.

What Guideline 2.5.1 actually means

Guideline 2.5.1 opens with one sentence that covers most rejections: "Apps may only use public APIs and must run on the currently shipping OS" (App Store Review Guidelines). The rest of the guideline adds three expectations:

  • Public APIs only. No private frameworks, undocumented C functions or selectors, private system notifications or private URL schemes.
  • Current OS, no deprecated tech. The app works on the OS customers run today and phases out "deprecated features, frameworks or technologies" Apple is retiring.
  • Intended purpose and disclosure. APIs are used for what they were built for, and the app description mentions the integration. Apple's own examples: HomeKit should provide home automation, and HealthKit should serve health and fitness and integrate with the Health app.

Problems surface at two stages:

WhereWhat you seeWhat it means
Upload / processingEmail with ITMS-90338: Non-public API usage or ITMS-90809: Deprecated API UsageAn automated scan of the uploaded binary. The email asks you to correct the issues and upload again. There is nobody to reply to, so the only fix is a new build.
App ReviewRejection citing "Guideline 2.5.1 - Performance - Software Requirements"A reviewer flagged a symbol, a framework used without a visible purpose, or behavior on the current OS. You can reply in App Store Connect.

Common triggers for a 2.5.1 rejection

  • A third-party SDK with non-public symbols. The most frequent case. Old analytics, ad or device-info libraries call undocumented functions. One example is the IOKit power-source functions (IOPSCopyPowerSourcesInfo), which Apple documents only for macOS and Mac Catalyst, not iOS. Static libraries are the hardest to spot because their code ends up inside your main executable.
  • Your method names collide with private selectors. The scan matches names, not intent. The ITMS-90338 email itself says renaming methods that match private APIs helps prevent future flags.
  • Private Settings URL schemes and system notifications. Opening prefs:root= or App-Prefs:root= to jump to Wi-Fi or Bluetooth settings, or listening to private Darwin notifications such as com.apple.springboard.lockcomplete, are reported as 2.5.1 non-public API findings.
  • UIWebView. Deprecated since iOS 12 in favor of WKWebView. New apps that reference it are rejected at upload with ITMS-90809. The reference often hides in an old Cordova or Xamarin version, an ad SDK, or a forgotten storyboard or nib.
  • HealthKit or HomeKit without a visible purpose. A typical message reads that the app "uses the HealthKit or CareKit APIs but does not clearly identify the HealthKit and CareKit functionality in the app's user interface". Leftover entitlements from a template or plugin cause the same problem.
  • Misused system services. VoIP pushes used to wake a chat app, or background modes without a matching feature (often cited under 2.5.4).
  • Not running on the current OS. Crashes or broken layouts on the newest iOS, an outdated toolchain, or a deployment target set to an unreleased iOS version.

Rejected or stuck? Talk to an App Specialist – for free.

Book a free consultation call: we look at your rejection or setup, explain the fastest way forward and tell you honestly whether you need us. Prefer to hand it off? Book our AI-powered + human-powered service and we take care of it.

How to find the offending symbol or SDK

The message names a binary and a list of symbols or selectors. A path like Frameworks/Foo.framework/Foo points to an embedded dynamic framework. The bare app name (or Runner for Flutter) means the main executable, which includes every statically linked library.

  1. Get the exact build you uploaded. Export the IPA from Xcode's Organizer or take it from your CI artifacts, then run unzip -q MyApp.ipa -d /tmp/ipa. Searching the .xcarchive itself also works and includes dSYMs.
  2. Search every file in the bundle. grep -rl "UIWebView" /tmp/ipa/Payload/MyApp.app lists every executable, framework and compiled nib that contains the string. Repeat with each name from the email.
  3. Inspect a suspicious framework. Run nm -u Frameworks/Foo.framework/Foo | grep -i webview to see imported symbols such as _OBJC_CLASS_$_UIWebView. Then run otool -L Frameworks/Foo.framework/Foo: any path containing /PrivateFrameworks/ is a red flag. otool -ov prints the Objective-C classes and methods a binary defines.
  4. Check selectors. Objective-C selector names are stored as plain strings, so strings -a MyApp | grep -Fx "flaggedSelector:" confirms whether a flagged selector is really in the binary. Copy the name exactly as listed, including colons.
  5. Trace main-executable hits to a library. Set Build Settings → Write Link Map File (LD_GENERATE_MAP_FILE = YES) and rebuild. Search the map file for the symbol: it shows which .o file or .a archive the symbol came from.
  6. Search the dependency sources. Run grep -rl "symbolName" Pods/ node_modules/ ios/ (plus the Flutter pub cache or Swift package checkouts) and map the hit to a package with npm ls <package> or flutter pub deps.
  7. Check your own code last. If the selector is defined in your sources, for example in a category or extension, rename it with a project prefix.

How to fix a 2.5.1 rejection step by step

  1. Update or replace the dependency. Move to the SDK's latest version and check its changelog for private API or UIWebView fixes. Replace abandoned libraries and delete unused pods and plugins. If a static library you can't update contains the symbol, it has to go.
  2. Swap private calls for public APIs. For Settings links, use UIApplication.openSettingsURLString (your app's settings page), openNotificationSettingsURLString (iOS 16+) or openDefaultApplicationsSettingsURLString (iOS 18.3+). There is no public way to open the Wi-Fi or Bluetooth pages, so tell users where to go instead. For battery information, use UIDevice battery monitoring instead of IOKit.
  3. Migrate UIWebView to WKWebView. Use Xcode's Find in Project for "UIWebView" and check storyboards and xibs too: Interface Builder files can still contain the old class.
  4. Fix framework purpose and disclosure. Make the integration visible in the UI and the description, or remove the capability completely in Signing & Capabilities (see the next section).
  5. Build with the required toolchain. Since April 28, 2026, uploads must be built with Xcode 26 or later using the iOS 26 SDK or later. Since September 9, 2026, iOS and iPadOS apps must also target iOS 13 or later. Apple has announced that iOS and iPadOS apps must be built with the iOS 27 SDK or later from April 2027. Check Apple's upcoming requirements for the current rule, and use a release or Release Candidate version of Xcode for App Store builds, not a beta.
  6. Test on the shipping OS. Run the release build on a device with the current major iOS version (iOS 27 as of October 2026) and on the oldest version you support. A crash on launch is usually rejected under Guideline 2.1 App Completeness.
  7. Verify and upload a new build. Increment CFBundleVersion, repeat the grep/nm checks on the new IPA, run Validate App in the Organizer, upload, and select the new build on the version page before you resubmit.

Intended purpose: HealthKit, HomeKit, CallKit and background modes

Here App Review checks behavior, not symbols: does what the app visibly does match the entitlements it ships with?

TechnologyWhat App Review expectsTypical miss
HealthKit / CareKitThe Health functionality is clearly identified in the UI, and the description mentions the Health app integrationData appears without attribution, or the entitlement is enabled but no feature uses it
HomeKitThe app actually provides home automationUsing HomeKit data for unrelated features
PushKit VoIP + CallKitEvery VoIP push reports an incoming call to CallKitUsing VoIP pushes to wake a messaging app
Background modes (UIBackgroundModes)Each mode matches a feature the reviewer can find, such as audio playback or navigationSilent audio or location updates used only to keep the app alive

For HealthKit, developers in Apple's forums report getting approved after adding a short explanation screen before the permission prompt, a permanent label such as "Health data sourced from Apple Health" next to the values, and a description sentence naming the Health app integration. Keep the purpose strings specific too (see the 5.1.1 guide).

For PushKit, Apple's documentation is explicit: on iOS 13 and later, the system terminates your app if it fails to report a call to CallKit. For anything that isn't a call, use standard notifications.

Flutter, React Native, Capacitor and game engines

In cross-platform apps the culprit is rarely in your Dart, JavaScript or C# code. It's usually in a native plugin or SDK.

  • React Native / Expo: native modules come in through CocoaPods from node_modules. Upgrade the package your grep found. With Expo, regenerate the native project (npx expo prebuild --clean) so stale pods don't linger.
  • Flutter: plugin code is compiled into Runner or shipped as frameworks. flutter pub outdated shows upgrade paths. Replace unmaintained plugins.
  • Capacitor / Cordova: check every plugin's iOS sources, not just the platform version. Old plugins are a frequent source of UIWebView and prefs:root references.
  • Unity / Unreal: upload a release (shipping) build and keep the engine and ad SDKs current. Development builds and outdated engine or ad SDK versions are the first things to rule out.

Plugin updates often fix other upload blockers at the same time, such as missing SDK privacy manifests. If your email also lists ITMS-91053, handle both in one pass.

How to respond to App Review (and when not to)

For ITMS upload emails there is nobody to reply to. Fix the binary, increment the build number and upload again.

For an App Review rejection, open Apps in App Store Connect, select the app, click the unresolved issues link at the top, click Resolve next to the submission and then Reply to App Review (Apple's help page). Name each flagged symbol or framework, what you changed and the new build number. For intended-purpose issues, point to the screen showing the integration and attach a screenshot.

If you believe the flag is a false positive, say so with evidence, such as nm output showing the symbol isn't in the binary. If App Review misunderstood how your app works, you can submit one appeal per submission to the App Review Board.

Never hide a private API with string obfuscation, dlsym or NSClassFromString. The standard 2.5.1 rejection text warns that continuing to use or conceal non-public APIs may lead to termination of your developer account.

For bug fix updates to a live app, the guidelines say fixes won't be delayed over guideline violations unless legal or safety issues are involved. You can ask App Review in App Store Connect to use this process and fix the issue in your next submission (App Review Guidelines). It doesn't help with ITMS upload errors, because those builds never reach review.

How to prevent 2.5.1 rejections

  • Scan every release build in CI. Unzip the IPA and fail the pipeline if grep -rl finds UIWebView, prefs:root, App-Prefs or /PrivateFrameworks/. It takes seconds. Expect the occasional false positive and treat a hit as a prompt to investigate.
  • Diff entitlements per release. codesign -d --entitlements - MyApp.app shows what you actually ship. A new HealthKit or HomeKit entitlement you didn't add on purpose is a warning sign.
  • Update dependencies on a schedule, not the week your release is due.
  • Pin and plan the toolchain. Pin Xcode on your CI runners, plan the iOS 27 SDK move before April 2027, and run TestFlight builds on the latest iOS release and its betas.
  • Keep descriptions honest. Mention every framework integration you rely on. The 2.3 metadata guide covers the rest of your listing.

AI-assisted checks speed up the tedious parts, like matching flagged symbols to dependency changelogs, while a human judges whether a framework's use matches its purpose. At appsubmitter.io we combine both. You can book a free consultation call to go through your rejection, or book the iOS service for pipeline setup, guidance on the fix and review communication. Code changes aren't included and are discussed and quoted upfront.

Template: how to reply to App Review

Adapt this template to your situation. Keep it factual, short and specific – and only claim what you have actually changed.

Hello App Review Team,

Thank you for reviewing [App Name]. Regarding Guideline 2.5.1, build [new build number] includes these changes:

1. [Flagged symbol or framework, e.g. _IOPSCopyPowerSourcesInfo in Frameworks/SomeSDK.framework]: [removed SomeSDK / updated SomeSDK to x.y / renamed our own method to xyz_methodName]. We checked the new binary with nm and strings, and the reference no longer appears.
2. [For HealthKit / HomeKit / background modes]: [the integration is now labeled on the (Screen name) screen and named in the app description] OR [we removed the unused entitlement].
3. The app is built with Xcode [version] and the iOS [version] SDK and was tested on iOS [current version].

[Optional: screenshot or screen recording attached showing (feature).]

Best regards,
[Your Name]

Checklist before you resubmit

  • Every symbol, selector and binary path from the email or rejection is traced to a specific library or source file.
  • grep -rl on the new IPA finds none of the flagged names, UIWebView, prefs:root, App-Prefs or /PrivateFrameworks/.
  • nm -u and otool -L on each embedded framework show no private symbols or private framework paths.
  • The SDKs and plugins involved are updated to their latest versions, and unused ones are removed.
  • Your own methods that matched private selector names are renamed with a project prefix.
  • Every entitlement and UIBackgroundModes value matches a feature a reviewer can find, and HealthKit or HomeKit use is labeled in the UI and named in the description.
  • The build uses a release or RC version of Xcode 26 or later with the iOS 26 SDK or later, and the deployment target is iOS 13 or later.
  • The release build launches and works on a device running the current major iOS version.
  • CFBundleVersion is incremented, Validate App passes, and the new build is selected on the version page.

Frequently asked questions

What does ITMS-90338 Non-public API usage mean if I never used a private API?
App Store Connect found a symbol or selector in your binary that matches a private Apple API. Usually it comes from a third-party SDK or plugin, often a static library compiled into your main executable. Sometimes your own method just shares a name with a private selector. Find the source with grep, nm or a link map, then update or remove the library, or rename the method.
Why do I get ITMS-90809 for UIWebView when my code doesn't use it?
The reference is usually in a dependency (an old ad SDK, Cordova plugin or Xamarin version) or in a storyboard or xib. Run grep -rl "UIWebView" on the unzipped IPA or the archive to see which file contains it, then update or remove that dependency.
Are app updates with UIWebView still accepted?
Apple stopped accepting new apps that use UIWebView in 2020. In October 2020 it extended the December 2020 deadline for updates and said a new deadline would be announced. Don't rely on that: UIWebView has been deprecated since iOS 12, so migrate to WKWebView as soon as an ITMS-90809 email arrives.
Can I hide a private API call with obfuscation or dlsym?
No. The 2.5.1 rejection text warns that continuing to use or conceal non-public APIs may lead to termination of your Apple Developer account. Use a public API or drop the feature.
Can my app open the Wi-Fi or Bluetooth settings page directly?
Not with a public API. URL schemes like App-Prefs:root=WIFI are non-public and have led to 2.5.1 rejections. Use UIApplication.openSettingsURLString to open your app's own settings page and tell users where to find the rest.
Which Xcode version do I need to upload to the App Store in 2026?
Since April 28, 2026, uploads must be built with Xcode 26 or later using the iOS 26 SDK or later, and since September 9, 2026, iOS apps must target iOS 13 or later. Apple has announced that the iOS 27 SDK will be required from April 2027. Check Apple's upcoming requirements page for the current rule.
How do I fix a 2.5.1 HealthKit rejection?
Make the Health integration obvious: add a short explanation before the permission prompt and a visible label where Health data appears, and mention the Health app integration in your description. If you don't use HealthKit, remove the capability so the entitlement disappears from the build.

Official source: App Store Review Guidelines – 2.5.1 Software Requirements. Store policies change regularly – always check the current version. This guide is independent advice and not affiliated with Apple or Google.

Talk to a real App Specialist

Turn your rejection into an approval

Book a free consultation call or let our App Specialists take over CI/CD, guidance on the fixes and the resubmission. Your app does not have to be 100% ready.

Back To School Sale prices until 31st October 2026. Prices in USD, excl. VAT.