Apple's privacy-manifest requirements have added a new release checklist for iOS teams. A repository can contain app-owned code that uses a Required-Reason API, one or more PrivacyInfo.xcprivacy files, and third-party SDKs that have their own privacy-manifest or signature requirements.
The painful time to discover an obvious gap is after preparing a release.
This guide shows a bounded, repeatable way to check repository evidence before App Store Connect upload. It does not replace Apple's final build validation or App Review.
What should you check before upload?
For a privacy-manifest preflight, three questions are especially useful:
- Does app-owned native source contain signals for Apple's Required-Reason API categories?
- Do the scanned
PrivacyInfo.xcprivacyfiles appear to cover those categories without obvious structure gaps? - Do dependency files contain SDKs on Apple's commonly used SDK list that deserve version-specific privacy-manifest/signature review?
Common App Store Connect messages developers search for include ITMS-91053 and ITMS-91056.
1. Find app-owned Required-Reason API signals
A simple example is app-owned use of UserDefaults:
let defaults = UserDefaults.standard
let isFirstLaunch = defaults.bool(forKey: "hasLaunchedBefore")
A static preflight can recognize that source signal as the category:
NSPrivacyAccessedAPICategoryUserDefaults
If the bounded repository scan finds that signal but does not find a relevant category declaration in the scanned privacy manifests, it can flag the file and line for review.
That is more useful than a generic warning because the developer gets repository evidence to inspect.
2. Review PrivacyInfo.xcprivacy structure
A privacy manifest can exist while still containing an obvious declaration gap.
A typical accessed-API entry has both the API category and a reasons array:
<key>NSPrivacyAccessedAPITypes</key>
<array>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>CA92.1</string>
</array>
</dict>
</array>
The reason code above is only an example for a specific Apple-defined UserDefaults use case. Do not copy a reason code merely because it makes a warning disappear. Confirm that the selected Apple-approved reason matches the app's actual behavior.
A repository preflight can flag obvious structure gaps, such as an accessed API type that appears to be missing a corresponding reasons declaration. The final built product remains authoritative.
3. Inventory Apple-listed SDK dependencies
Privacy-manifest work is not limited to app-owned Swift or Objective-C.
Useful dependency evidence can appear in files such as:
-
PodfileandPodfile.lock -
Package.swiftandPackage.resolved pubspec.yamlpackage.json
A preflight can inventory names that appear on Apple's commonly used SDK list, such as Firebase modules, Flutter, hermes, Alamofire, Kingfisher, Lottie, RealmSwift, and others.
This should be treated as a review signal, not proof that a submission is broken. The exact dependency version packaged into the final app determines whether the expected privacy manifest and, when applicable, SDK signature are present.
Run the same preflight locally
If you want to check a repository without connecting it to a web service, run the AccessVerdict CLI locally:
npx @accessverdict/cli scan .
For machine-readable output:
npx @accessverdict/cli scan . --json
For CI, --strict makes review-only findings return a non-zero exit code:
npx @accessverdict/cli scan . --strict
The CLI scans repository text files on your machine and does not upload repository source code to AccessVerdict. Its optional anonymous usage telemetry contains only a random installation id, CLI version, outcome class, and whether --json or --strict was used. It does not send repository names, paths, filenames, source code, or finding text. Set DO_NOT_TRACK=1 or ACCESSVERDICT_TELEMETRY=0 to disable it.
You can also try the CLI against the five synthetic cases in the companion GitHub repository.
Put the preflight in GitHub Actions
A minimal CI check can run the same public CLI on pull requests:
name: App Store privacy preflight
on:
pull_request:
jobs:
privacy-preflight:
runs-on: ubuntu-latest
env:
DO_NOT_TRACK: "1"
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 24
- run: npx --yes @accessverdict/cli scan . --strict
The companion repository runs a live version of this workflow and verifies both a clean fixture and an expected blocker fixture:
The demo disables telemetry so automated example runs do not get counted as real CLI usage.
A deterministic repository preflight
AccessVerdict runs bounded, read-only repository checks and reports file-level evidence.
Its current preflight focuses on:
-
PrivacyInfo.xcprivacypresence and obvious structure gaps; - app-owned Required-Reason API source signals;
- category coverage in scanned privacy manifests;
- common Apple-listed SDK dependency inventory;
- repository-scope uncertainty.
The scanner does not execute customer code and does not claim to reproduce App Review.
The useful workflow is:
- Scan the repository.
- Review the file-level finding.
- Confirm the requirement in Apple's documentation.
- Fix the project locally.
- Run the same preflight again.
- Confirm the final Xcode privacy report and built product before submission.
Five small cases you can inspect
A companion public repository contains five minimal synthetic cases:
- missing privacy manifest;
- required-reason API category missing from an existing manifest;
- obvious manifest reasons-structure gap;
- Apple-listed SDK dependency review;
- clean preflight case.
Browse the cases on GitHub: ios-privacy-manifest-preflight-cases.
Why not just ask an AI assistant?
AI is useful for explaining Apple documentation and helping write a fix.
A release gate has a different job: run the same bounded checks every time and return repeatable evidence. The two workflows complement each other.
Use an assistant to understand or edit. Use a deterministic preflight to rerun the release check.
Final checklist
Before an App Store upload:
- inspect app-owned Required-Reason API usage;
- review the privacy-manifest evidence in the repository;
- inspect the exact third-party SDK versions used by the build;
- rerun the same preflight after changes;
- confirm the final Xcode-generated privacy report and submitted build.
You can run the repository preflight at accessverdict.dev.
AccessVerdict is an advisory static-analysis tool and is not affiliated with Apple. A clean repository scan is not a guarantee of App Store approval.
Top comments (2)
The distinction between repository evidence and the final built product is the important guardrail here. A practical next check would be to archive the preflight report with the build artifact, so the App Store warning can be traced back to the exact dependency lockfile and manifest set that were reviewed.
Thanks — I agree. Keeping repository evidence separate from the final built product is an important boundary.
Archiving the preflight report alongside the build artifact is a good next step. The CLI already supports
--json, so a CI workflow could preserve that report together with the dependency lockfiles and privacy manifests used for the release.I haven't automated the binding between a specific preflight report and a specific build artifact yet, but that traceability is exactly the kind of release evidence I'd like to add next.