Your archive uploaded, the ingestion email returned ITMS-91056 with the phrase Invalid privacy manifest, and the body of the email named a file at Frameworks/Reachability.framework/ReachabilitySwift.bundle/PrivacyInfo.xcprivacy. The build sits in the Invalid Binary state. The question is which key or value inside that shipped manifest the audit refused, why the file ended up in your archive in the first place, and the smallest change that gets the next upload to a green ingestion.
Short answer
App Store Connect returns ITMS-91056 when a PrivacyInfo.xcprivacy file shipped inside an embedded framework, bundle, or xcframework does not parse against Apple's schema for the four recognised top-level keys: NSPrivacyTracking, NSPrivacyTrackingDomains, NSPrivacyCollectedDataTypes, and NSPrivacyAccessedAPITypes. The ingestion email prints the path inside the .app, in this case Frameworks/Reachability.framework/ReachabilitySwift.bundle/PrivacyInfo.xcprivacy. The fix is to pull a fresh release of the framework whose manifest passes validation, swap to a maintained alternative, or drop the dependency. An app-level manifest in the main target cannot override a malformed file nested inside a vendored bundle. Apple's Privacy manifest files reference lists the recognised keys and value types.
What you should know
- The audit reads every PrivacyInfo.xcprivacy inside the .app, not only the one in your main target. A manifest nested under a third-party framework or its resource bundle is validated on its own merits.
- The error message names the path, not the failing key. Identifying which entry the audit refused is a separate plist inspection step run with plutil or PlistBuddy on the file inside the archive.
- The Reachability name appears on Apple's list of SDKs that must ship a signed privacy manifest. ReachabilitySwift, the Swift port, ships a manifest defensively because of the name match.
- Only four top-level keys are recognised. NSPrivacyTracking is a Boolean, NSPrivacyTrackingDomains is an array of strings, and NSPrivacyCollectedDataTypes plus NSPrivacyAccessedAPITypes are arrays of dictionaries with a fixed set of inner keys.
- The framework owner is responsible for the file inside the framework. Updating to a release where the maintainer corrected the manifest is the cleaner answer than patching a vendored copy.
- The build number must change between uploads. Re-uploading at the same number leaves the Invalid Binary record in place even after the fix lands.
Why does App Store Connect refuse a manifest the SDK author already shipped?
App Store Connect parses every PrivacyInfo.xcprivacy in the upload through the same validator and compares each key and value against the schema published in Apple's Privacy manifest files reference. A typo in a key name, an unrecognised enum value under NSPrivacyAccessedAPIType, a category that no longer exists, or a value with the wrong plist type (a string where the schema expects a Boolean) is enough to fail the parse. The audit treats the file as one unit: a single invalid entry fails the whole manifest.
The mechanism matters because the ingestion email mentions only the path, not the line. The maintainer of Reachability.swift tracked the same pattern in issue 421, where developers reported the ITMS-91056 email naming the ReachabilitySwift.bundle path while their own app target carried a valid manifest. The bundle inside the framework was the file the audit refused, even though the main target was clean.
There is one more reason the file shows up at all. Apple's list of third-party SDKs that require a signed privacy manifest includes the Reachability name. ReachabilitySwift, the Swift port maintained by Ashley Mills, ships under the SwiftPM product name ReachabilitySwift and the framework name Reachability, so maintainers added a manifest defensively. When that defensive file does not parse, the audit fires on the shipped bundle even though the app developer never wrote a line of it.
Which keys and values does the audit actually accept?
The privacy manifest plist root is a dictionary. Only four top-level keys are recognised: NSPrivacyTracking, NSPrivacyTrackingDomains, NSPrivacyCollectedDataTypes, and NSPrivacyAccessedAPITypes. Any other key fails validation. The values follow a fixed shape per Apple's reference page.
| Top-level key | Type | Notes |
|---|---|---|
| NSPrivacyTracking | Boolean | true or false. Always present, even when the rest of the manifest is empty. |
| NSPrivacyTrackingDomains | Array of strings | Domains contacted for tracking. Empty array is valid when tracking is false. |
| NSPrivacyCollectedDataTypes | Array of dictionaries | Each entry has NSPrivacyCollectedDataType (string), NSPrivacyCollectedDataTypeLinked (Boolean), NSPrivacyCollectedDataTypeTracking (Boolean), and NSPrivacyCollectedDataTypePurposes (array of strings). |
| NSPrivacyAccessedAPITypes | Array of dictionaries | Each entry has NSPrivacyAccessedAPIType (one of the five published category strings) and NSPrivacyAccessedAPITypeReasons (array of approved reason codes for that category). |
The five recognised values for NSPrivacyAccessedAPIType are NSPrivacyAccessedAPICategoryUserDefaults, NSPrivacyAccessedAPICategoryFileTimestamp, NSPrivacyAccessedAPICategorySystemBootTime, NSPrivacyAccessedAPICategoryDiskSpace, and NSPrivacyAccessedAPICategoryActiveKeyboards. A misspelt category (NSPrivacyAccessedAPICategoryUserDefault without the trailing s, for example) is the single most common cause of ITMS-91056 in a vendored manifest. The approved reason codes follow the format four-character.one (CA92.1, 1C8F.1, 35F9.1, and so on) listed on Apple's Describing use of required reason API page.
To inspect the file the email named, open the archived .app in Finder, navigate to the framework path the email printed, and run plutil against the .xcprivacy file:
plutil -lint YourApp.app/Frameworks/Reachability.framework/ReachabilitySwift.bundle/PrivacyInfo.xcprivacy
plutil -p YourApp.app/Frameworks/Reachability.framework/ReachabilitySwift.bundle/PrivacyInfo.xcprivacy
The first command exits non-zero on a structural plist error. The second prints the file in a human-readable form so the offending key or value is visible. A typo in a category string, a top-level key the schema does not list, or a value of the wrong plist type tends to surface in seconds.
How do I tell which dependency pulled in ReachabilitySwift?
The bundle path inside the .app names the framework, not the package that links it. ReachabilitySwift sits inside Reachability.framework, and several upstream packages add it as a transitive dependency. A Swift Package Manager project pulls it through Package.resolved; a CocoaPods project records it in Podfile.lock; a Carthage project lists it in Cartfile.resolved.
The fastest scan is a single grep across the lock files at the project root:
grep -i reachability Package.resolved Podfile.lock Cartfile.resolved 2>/dev/null
The output names the resolver entry and the version. Common parent packages that surface ReachabilitySwift include the Flutter connectivity_plus plugin, ReactiveSwift extensions, and older networking helpers ported from the Apple sample code. When the parent is a Flutter plugin, the manifest file lives inside the iOS subspec the plugin vendors, so updating the plugin to a release where its own manifest is corrected is the path with the smallest blast radius.
When the parent is your own project linking ReachabilitySwift directly, the choice is between updating to a tag that ships a fixed manifest, switching to a maintained fork, or replacing the dependency with Apple's NWPathMonitor API on iOS 12 and later. NWPathMonitor covers most cases the older library handled, removes the manifest issue at the source, and aligns with Apple's preferred path for the same functionality.
What is the smallest fix that clears the Invalid Binary?
Three changes typically land together. None of them touches the main app manifest.
- Update the framework to a release whose manifest validates. Pull the latest tag of the dependency, regenerate the lock file (
pod install --repo-updatefor CocoaPods,xcodebuild -resolvePackageDependenciesfor SPM), clean the build folder, and archive a fresh build with an incremented build number. - If no release is available yet, replace the file inside the vendored framework with a minimal valid manifest. A two-key plist with
NSPrivacyTrackingset to false andNSPrivacyTrackingDomainsset to an empty array passes the audit when the library does not collect data or call a required reason API. Modifying a vendored framework is a short-term patch; the change has to be reapplied on every clean install until the maintainer ships a correct file upstream. - Bump CFBundleVersion before re-uploading. App Store Connect keys the Invalid Binary record to the build number. Re-uploading at the same number returns the same email even after the underlying file is fixed.
For builders who want an external automated read across every embedded framework before the next upload, PTKD.com (https://ptkd.com) is one of the platforms that inspects compiled IPA bundles and flags PrivacyInfo.xcprivacy files whose keys, categories, or reason codes fall outside Apple's schema. It surfaces the offending entry before App Store Connect does, which shortens the loop between an Invalid Binary email and a clean ingestion.
How is ITMS-91056 different from ITMS-91053 and ITMS-91065?
The three audits share the same enforcement source but report different causes. Keeping them separate saves time on the next iteration.
| Code | What the audit refused | Typical fix |
|---|---|---|
| ITMS-91053 | A symbol on Apple's required reason API list is in the binary, and no manifest declares an approved reason code for the matching category. | Add a category and reason code entry to the manifest of whichever target or framework links the symbol. |
| ITMS-91056 | A PrivacyInfo.xcprivacy file inside the upload contains a key or value the schema does not recognise. | Replace or patch the malformed manifest, usually inside a third-party framework. |
| ITMS-91065 | A privacy manifest at the named path is present but not signed when Apple's list requires a signature. | Update to a release of the framework that ships a signature, or remove the framework. |
ITMS-91053 is fixed by adding a missing entry; ITMS-91056 is fixed by repairing a broken one; ITMS-91065 is fixed by upgrading or removing the framework that should carry a signed file. Mixing them up sends the next iteration into the wrong corner.
What to watch out for
A few patterns turn a one-build fix into a multi-day loop.
- Patching a vendored manifest without pinning the dependency. A clean checkout or fresh pod install overwrites the edit. Pin the version in Podfile or Package.swift and keep a note in the repo so the patch survives the next install.
- Editing the manifest in the project but not the one inside the archive. The audit reads the file that ends up in the .app, not the one in the source tree. Build, open the archive, and inspect the framework path the email named.
- Assuming the app-level PrivacyInfo.xcprivacy covers the nested one. It does not. The audit treats each manifest in the upload as its own unit.
- Treating ITMS-91056 as a signing problem. That is ITMS-91065. ITMS-91056 fires on the contents of the file, not its signature.
- Re-uploading at the same build number. App Store Connect returns the same error on the cached Invalid Binary record.
Key takeaways
- ITMS-91056 fires on the contents of a PrivacyInfo.xcprivacy file, and the email names the path inside the archive that failed the schema check.
- The ReachabilitySwift bundle path appears because the Reachability name sits on Apple's list of SDKs that must ship a manifest, and several upstream Flutter and Swift packages vendor the framework transitively.
- The four top-level keys (NSPrivacyTracking, NSPrivacyTrackingDomains, NSPrivacyCollectedDataTypes, NSPrivacyAccessedAPITypes) are fixed; anything else fails the parse.
- The smallest fix is usually a framework update plus a build-number bump, not a change to the main target manifest.
- Some teams outsource the pre-submission manifest sweep across every embedded framework to platforms like PTKD.com (https://ptkd.com), which surfaces malformed entries before App Store Connect rejects the upload.


