Ever since Apple effectively required PrivacyInfo.xcprivacy submissions in 2024, every release of my wallpaper apps has involved the same small chore: re-reading the NSPrivacyAccessedAPITypes entries and patching the file by hand. I've been shipping indie iOS apps since 2014, and my catalog has accumulated more than 50 million downloads across the App Store and Google Play. None of that scales the time I spend on a per-release privacy review.
Last month I started letting Gemini 2.5 Pro handle parts of that review. After thirty days, the quiet conclusion I've arrived at is this: rather than asking the model to generate the whole manifest, it works far better as a "diff reader" that lists only the missing entries between my current file and the manifest that ships with each SDK. These are my notes.
Why I brought Gemini into the privacy manifest work
The trigger was an App Store Connect warning that appeared after I bumped my AdMob SDK version. The new SDK had declared additional Required Reasons API entries, and my app's PrivacyInfo.xcprivacy no longer covered them. The warning didn't block delivery, but reconciling it by hand — across three layers of manifests — kept eating my Sunday review time.
The recurring workload looked like this:
- Cross-check three layers of
xcprivacy: the app target, each first-party module, and every third-party SDK - Pair every
NSPrivacyAccessedAPITypewith the rightNSPrivacyAccessedAPITypeReasonscodes - When a new SDK is added, locate the correct reason code in Apple's Required Reasons API list
- Strip duplicated declarations from the app-level manifest
Of these, "reconciling against Apple's reason-code list" and "spotting what's missing across multiple xcprivacy files" felt mechanical enough to delegate. Confirming where my own source actually calls those APIs, on the other hand, is the kind of task where I want to stay vigilant about hallucinations.
What I learned during the first week
For the first few days I let Gemini generate the whole manifest from scratch. The resulting XML looked plausible, but the reason codes (the four-character tokens like CA92.1 and 35F9.1) sometimes drifted from what the SDKs actually declared. I ended up reconciling everything by hand anyway, which defeated the point.
So I split the workflow into three steps:
- Diff two manifests with Gemini. I paste my current
xcprivacyand the manifest shipped with the new SDK side-by-side, and ask the model to list only the missingNSPrivacyAccessedAPITypeentries. - Match against Apple's Required Reasons API list. I share the list with Gemini and ask for one or two candidate reason codes per new entry. I keep the final call myself.
- Confirm in source code by hand. For things like
UserDefaultsandFileTimestamp, I grep my own app target and pick a reason that matches the actual call site.
That split cut my per-release privacy review from roughly forty minutes to ten. Not a full automation, but enough to free up the evenings I used to spend squinting at XML.
The prompt I ended up using
Once I'd narrowed the scope, the prompt template settled fairly quickly. The trick that made the biggest difference was constraining the output to a reasoned diff table rather than rewritten XML.
You are a reviewer for Apple Privacy Manifests.
Compare the two xcprivacy files below.
[File A: my current app xcprivacy]
<plist>
...trimmed...
</plist>
[File B: xcprivacy bundled with the new SDK]
<plist>
...trimmed...
</plist>
Task:
- List NSPrivacyAccessedAPIType entries present in B but missing from A.
- For each, suggest one or two NSPrivacyAccessedAPITypeReasons codes
(in Apple's official format) that A would need to add.
- Prefer the reason codes that appear in B over guesses.
- Output format: a Markdown table with columns
API / suggested reason codes / evidence in B.
I then translate that table into the actual xcprivacy by hand, letting Xcode's Property List Editor do the final formatting. Asking the model to rewrite XML directly tended to break quote styles or attribute ordering in subtle ways.
This pattern — translating a generation task into a reading task before handing it to the model — is something I find true with Claude and ChatGPT as well. Hallucinations drop sharply once the model is asked to find rather than to invent.
Three things that didn't work
A few experiments that I quietly abandoned:
First, generating the manifest from Swift source. I asked Gemini to grep UserDefaults references and emit matching xcprivacy entries. The result was overwhelmed by references inside third-party libraries, and even when I scoped the prompt to my app target only, I still had to verify everything by hand. Net time saved: roughly zero.
Second, batching multiple apps together. Pasting five XML files at once degraded per-file precision and started missing entries. Processing one manifest at a time produced better diffs. Long context windows are useful, but reading deeply across one document is a different skill from skimming five of them — that's an obvious lesson now, but I had to bump my head on it.
Third, syncing with App Privacy Details on App Store Connect. The two surfaces should agree, but App Privacy Details requires reading Apple's documentation alongside their phrasing of each question. Asking Gemini to summarize the docs gave me a version I still wanted to verify against the source, so I gave up and just read Apple's English documentation directly. Some friction isn't worth automating.
After thirty days
To be honest, the total time I spend on privacy manifest work has dropped by about half — not the ninety-percent reduction I'd quietly hoped for. But across the dozen-plus releases I push a month as a solo developer, "half" still adds up. My grandfathers on both sides were temple carpenters, and watching them work as a child left me with a soft preference for tools that leave the careful judgment to a person and offload only the repetitive reading. This Privacy Manifest workflow ended up shaped the same way.
A short summary of where I've landed:
- Hand to Gemini 2.5 Pro: diffing
xcprivacyfiles, cross-referencing Required Reasons API codes - Keep myself: the final
NSPrivacyAccessedAPITypechoice in app code, App Privacy Details answers - Don't delegate: end-to-end manifest generation from source, batch-processing multiple apps
Next month I'd like to try wiring Claude in Chrome to read App Store Connect warnings and feed them back into Gemini's diff prompt. If you're maintaining an indie iOS catalog of your own, I hope some of this saves you the half-hour I lost relearning it. Thanks for reading.