Skip to main content

Solved: Flutter App Rejected (ITMS-91061) Missing Privacy Manifest

 You have uploaded your Flutter iOS build to TestFlight or the App Store, and the binary was immediately rejected or flagged with the following warning:

ITMS-91061: Missing Privacy Manifest – Your app includes "flutter_local_notifications", which accesses the following API categories: NSPrivacyAccessedAPICategoryUserDefaults. However, a privacy manifest file for this framework could not be loaded.

You likely already have a PrivacyInfo.xcprivacy file in your project. You may have even audited your own native code. Yet, the rejection persists.

This occurs because Apple’s static analyzer detects usage of "Required Reason APIs" (specifically UserDefaults) inside the compiled binary of a transitive dependency (the Flutter plugin), but that dependency failed to bundle its own privacy manifest during the CocoaPods/Framework embedding process.

Here is the root cause analysis and the definitive fix to unblock your release.

Root Cause Analysis: The Transitive Dependency Gap

Starting Spring 2024, Apple requires that apps and third-party SDKs declare why they use specific sensitive APIs. This is enforced via the PrivacyInfo.xcprivacy file.

When you compile a Flutter app, plugins like flutter_local_notifications are compiled as embedded frameworks or static libraries. The App Store Connect validator scans the symbol table of your binary.

  1. Symbol Detection: The validator detects symbols like NSUserDefaults within the compiled binary of flutter_local_notifications.
  2. Manifest Lookup: It looks for a PrivacyInfo.xcprivacy specifically associated with that bundle or the main app bundle that accounts for this usage.
  3. The Failure: Many Flutter plugins (especially older versions) use UserDefaults to persist scheduled notification IDs but do not ship a privacy manifest. Consequently, the usage is "undeclared."

Even if your main Runner project has a manifest, if it doesn't explicitly declare the plugin's specific reasons for using the API, the build is flagged.

The Solution

To resolve ITMS-91061, you must manually declare the API usage required by the offending plugin in your host application's PrivacyInfo.xcprivacy file. This overrides the missing manifest from the dependency.

Step 1: Verify the Usage (Optional but Recommended)

Before applying the fix, verify that the plugin is indeed the culprit using nm to inspect the symbols in the compiled framework.

Run this in your terminal inside your ios directory after a build:

# Locate the framework (path varies based on build config)
cd Build/Products/Release-iphoneos/flutter_local_notifications/flutter_local_notifications.framework

# Grep for UserDefaults symbols
nm -g flutter_local_notifications | grep "NSUserDefaults"

If you see output like _OBJC_CLASS_$_NSUserDefaults, the plugin is linking against the API.

Step 2: Create or Update PrivacyInfo.xcprivacy

You must add the User Defaults category to your main application's privacy manifest.

  1. Open ios/Runner.xcworkspace in Xcode.
  2. Locate Runner/PrivacyInfo.xcprivacy. If it doesn't exist, create it (File > New > File > Resource > Privacy Manifest).
  3. Ensure the file is checked under Target Membership for the Runner target.

Add the following configuration. This XML declares that your app (via its dependencies) uses UserDefaults to store app-specific data (Reason CA92).

Right-click PrivacyInfo.xcprivacy > Open As > Source Code, and paste/merge the following NSPrivacyAccessedAPITypes array:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <!-- Other existing keys (like NSPrivacyTracking, NSPrivacyCollectedDataTypes) go here -->

    <key>NSPrivacyAccessedAPITypes</key>
    <array>
        <dict>
            <!-- 1. The Category Code for UserDefaults -->
            <key>NSPrivacyAccessedAPIType</key>
            <string>NSPrivacyAccessedAPICategoryUserDefaults</string>
            
            <!-- 2. The Required Reasons -->
            <key>NSPrivacyAccessedAPITypeReasons</key>
            <array>
                <!-- CA92: Access info from same app. 
                     Commonly used by flutter_local_notifications to store IDs. -->
                <key>String</key>
                <string>CA92</string>
            </array>
        </dict>
    </array>
</dict>
</plist>

Step 3: Verify the Manifest Generation

Simply adding the file isn't enough; you must verify that Xcode is aggregating it correctly into the final archive.

  1. In Xcode, go to Product > Archive.
  2. Once the archive is generated, right-click the archive in the Organizer and select Generate Privacy Report.
  3. Save the PDF.
  4. Open the PDF and look for the "App Privacy Configuration" section.
  5. Confirm that UserDefaults is listed with reason CA92.

Step 4: Clean and Re-deploy

Flutter builds often cache intermediate build artifacts. To ensure the new manifest is bundled correctly:

flutter clean
flutter pub get
cd ios
pod install --repo-update
cd ..
flutter build ios --release

Upload the new build. The ITMS-91061 error will be resolved.

Why This Fix Works

Apple allows the main app bundle (the "Host") to declare privacy usage on behalf of statically linked libraries or embedded frameworks that lack their own signatures.

By explicitly adding NSPrivacyAccessedAPICategoryUserDefaults with reason CA92 to your Runner target, you are legally attesting to Apple that:

  1. Usage: The app accesses UserDefaults.
  2. Scope: The data accessed is limited to the app's own sandbox (which covers the plugin's behavior of storing local notification IDs).

This satisfies the static analyzer's requirement for a declared reason corresponding to the detected symbol usage.

Conclusion

While updating dependencies is the ideal long-term strategy, flutter_local_notifications and similar plugins are critical infrastructure where major version bumps can introduce breaking changes. Manually patching the PrivacyInfo.xcprivacy at the Runner level is the correct, compliant, and immediate engineering solution to unblock production releases.