macOS Privacy Manifests: Bundle Placement, SDK Reports, and Release Audits
Audit macOS PrivacyInfo.xcprivacy placement, SDK-owned disclosures, Xcode privacy reports, and the gap between plist linting and policy validation.
A macOS privacy manifest is easy to treat as one more property list in a project. That is not enough for a release audit. The file must be named and packaged where Apple expects it, each app or SDK must describe its own practices, and the archive must contain the resources that Xcode actually used to prepare its privacy report. A source-tree file that never made it into the built .app is not a useful disclosure.
This guide focuses on the macOS bundle and SDK boundaries. It covers PrivacyInfo.xcprivacy, Xcode’s aggregate privacy report, and checks that can be run against an archived app. It does not treat a manifest as a permission grant, a runtime privacy control, or proof that the binary behaves as declared. App Store Connect requirements also differ from direct distribution, so check Apple’s current submission guidance for the channel and SDK versions you ship.
The manifest is a bundled property list
Apple names the file PrivacyInfo.xcprivacy. It is a property list resource, not an entitlement and not the app’s Info.plist. For a macOS app, Apple’s bundle-placement table puts it at MyApp.app/Contents/Resources/PrivacyInfo.xcprivacy. For a macOS framework, the documented location is FrameworkName.framework/Versions/A/Resources/PrivacyInfo.xcprivacy. Mac Catalyst follows the macOS app location in Apple’s placement guidance.
That distinction matters in a release pipeline with multiple targets. Adding a file to the repository or an Xcode project navigator does not by itself prove that the target processes it as a resource. Apple says the manifest must be added to the target’s resources for Xcode to include it in the privacy report. Inspect the archive, not just the project tree, and check each embedded framework or SDK bundle that owns a manifest.
For Swift packages, Apple calls out an easy-to-miss packaging detail: Xcode does not recognize privacy manifest files as package resources by default. Declare the resource explicitly in the target that owns it:
targets: [
.target(
name: "TelemetryCore",
resources: [
.process("PrivacyInfo.xcprivacy")
]
)
]
The path is relative to the target’s source directory when the file is stored there. If a package uses a separate Resources directory, declare that directory instead and verify where the built product places the manifest. Do not assume that a package-level file automatically describes every product target in the package; resource ownership follows the target and bundle that ships the code.
Keep app and SDK disclosures separate
Apple’s manifest model has separate owners. The app records the data practices of code it owns. A third-party SDK records the data its own code collects, including cases where it enables the host app to collect data or contacts tracking domains. Apple’s data-use guide says the app’s manifest does not need to duplicate data collected by a third-party SDK linked into the app; the SDK should provide its own manifest. That division makes dependency updates auditable instead of turning the app manifest into an untraceable union of declarations.
At a high level, NSPrivacyCollectedDataTypes describes each collected data type and its linked-to-user, tracking, and purpose attributes. NSPrivacyTracking and NSPrivacyTrackingDomains describe tracking-related behavior and domains when applicable. These fields are declarations that should match the actual data flows and product behavior. They do not make an inaccurate collection practice compliant, grant an app network access, or replace user-facing notices and consent where those are required.
Apple’s current required-reason documentation is especially important to scope correctly. Its overview explicitly lists required-reason API declarations for iOS, iPadOS, tvOS, visionOS, and watchOS. It does not list macOS in that platform set. Do not infer a macOS submission obligation solely because the generic property-list schema includes NSPrivacyAccessedAPITypes, or copy an iOS manifest into a Mac target without checking the current platform-specific documentation. A cross-platform SDK may still need reason declarations for the other platform variants it ships. Recheck Apple’s live guidance when the supported platforms or submission channel change.
The requirements for particular third-party SDKs are another separate layer. Apple’s current list identifies SDKs that require a privacy manifest and, for binary dependencies in the applicable submissions, a signature. The list can change, includes repackaged versions, and is tied to App Store Connect submissions. Record the SDK name, exact version, delivery format, and applicable Apple requirement alongside the dependency lock or release bill of materials. Do not turn that list into a blanket claim that every macOS library has the same signature requirement.
Build an archive-level check
The release artifact is the right object to inspect. After creating a macOS archive, locate the app that will be distributed and check any app-level manifest plus nested framework manifests. For a conventional Xcode archive, a starting point is:
APP="build/MyApp.xcarchive/Products/Applications/MyApp.app"
APP_MANIFEST="$APP/Contents/Resources/PrivacyInfo.xcprivacy"
if [ ! -d "$APP" ]; then
echo "Archived app not found: $APP" >&2
exit 1
fi
if [ -f "$APP_MANIFEST" ]; then
: # It will be syntax-checked with every discovered manifest below.
elif [ "${REQUIRE_APP_MANIFEST:-0}" = "1" ]; then
echo "Missing expected app privacy manifest: $APP_MANIFEST" >&2
exit 1
fi
manifest_list=$(mktemp "${TMPDIR:-/tmp}/privacy-manifests.XXXXXX") || exit 1
trap 'rm -f "$manifest_list"' EXIT
if ! find "$APP" -type f -name PrivacyInfo.xcprivacy -print0 > "$manifest_list"; then
echo "Could not enumerate every privacy manifest in: $APP" >&2
exit 1
fi
manifest_found=0
while IFS= read -r -d '' manifest; do
manifest_found=1
if ! plutil -lint "$manifest"; then
exit 1
fi
done < "$manifest_list"
if [ "$manifest_found" -eq 0 ] && [ "${REQUIRE_ANY_MANIFEST:-0}" = "1" ]; then
echo "No privacy manifests found in an artifact that requires one" >&2
exit 1
fi
Run this example with Bash on macOS. The archive directory and complete find traversal are checked explicitly, and a temporary NUL-delimited manifest list lets the script detect an enumeration error rather than silently accepting partial output. Every listed file is linted, and any failed plutil command exits nonzero. Set REQUIRE_APP_MANIFEST=1 only when the app itself is expected to ship one. Set REQUIRE_ANY_MANIFEST=1 in a release channel or target configuration where the current Apple submission rules or the product’s release policy require at least one manifest in the archive. These gates answer different questions: the app-owned manifest may be optional while a nested SDK manifest is required, or vice versa. Every discovered manifest is linted, including SDK and extension manifests. An app-level manifest is not a universal prerequisite merely because the product targets macOS; determine expectations from the current distribution channel, target, collection practices, and Apple requirements.
plutil -lint verifies property-list syntax. It does not prove that keys, enumerated values, collection purposes, platform declarations, or SDK claims are acceptable. Apple warns that App Store Connect rejects submissions containing invalid privacy manifests, including files with unexpected keys or values. Treat syntax checking as an early build gate, not a policy validator. A production audit should compare each declared data type and purpose with the feature implementation, network behavior, analytics configuration, and the current Apple schema.
Then use Xcode’s archive-based privacy report as a second, different check. In Organizer, Control-click the archive and choose Generate Privacy Report. Apple says Xcode aggregates manifests from the app and linked third-party SDKs into that report. Review whether expected SDKs appear and whether their reported practices match vendor documentation and your integration configuration. The report is an input to accurate App Store privacy details; it does not automatically author those details or certify that the app’s runtime behavior matches its declarations.
For release automation, retain the archive identifier, Xcode version, dependency lock state, list of discovered manifest paths, syntax-check output, and the generated report with the release evidence. If a report omits an SDK, inspect the SDK product itself and its packaging instructions before patching the host manifest. Duplicating an SDK’s disclosure in the app can hide the packaging defect rather than fix it.
Diagnose common release failures
| Symptom | Likely boundary to inspect | Safe next check |
|---|---|---|
| The source file exists but the report omits it | Resource membership or wrong bundle path | Inspect the archived .app and target build resources |
| An SDK is present but contributes no manifest | SDK version or package/framework resource packaging | Inspect the embedded SDK bundle and ask its publisher for the matching manifest |
plutil -lint passes but App Store validation fails |
Schema keys or enumerated values, not XML syntax | Compare the file with Apple’s current manifest schema and invalid-manifest troubleshooting note |
| The app manifest repeats SDK data declarations | Ownership was collapsed across dependency boundaries | Keep app and SDK manifests with the code owner responsible for each collection path |
| An iOS-required-reason recipe appears in a macOS-only target | Platform rules were copied without checking scope | Consult Apple’s current platform-specific required-reason documentation |
Do not “fix” a missing SDK manifest by guessing its data practices or fabricating a reason code. Update, replace, or remove the dependency through its documented release channel, then rebuild and regenerate the report. For an invalid manifest supplied by a vendor, Apple’s guidance is to contact the SDK developer for a corrected version.
Make privacy evidence part of the release record
The practical contract is a chain: source manifest, target resource membership, final bundle placement, SDK-owned manifests, Xcode’s archive report, and the submission disclosure. A check at only one stage can miss a later packaging or ownership failure. Keep that chain visible in CI and release review, and revisit Apple’s live documentation when adding an SDK, changing the supported platforms, or preparing a new App Store submission.
Related:
- plutil on macOS: Validate and Convert Property Lists Without Losing Types
- macOS Installer Packages: Build, Sign, Notarize, and Verify .pkg Releases
Sources:
- Apple Developer Documentation: Privacy manifest files
- Apple Developer Documentation: Adding a privacy manifest to your app or third-party SDK
- Apple Developer Documentation: Describing data use in privacy manifests
- Apple Developer Documentation: Describing use of required reason API
- Apple Developer Support: Third-party SDK requirements
- Apple Developer Technical Note TN3181: Debugging an invalid privacy manifest