●FORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtask●LIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its own●MCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MS●PLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callback●SONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15●IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as October●FORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtask●LIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its own●MCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MS●PLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callback●SONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15●IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as October
Running Apple Privacy Manifest as an Indie Developer — Catching Dependency Drift with Claude Code
Apple Privacy Manifest (PrivacyInfo.xcprivacy) is a quietly painful area for indie iOS developers. Drawing on twelve years of running iOS apps with over 50 million combined downloads, this article walks through how I use Claude Code to detect drift, respond to rejections, and bake the whole flow into CI.
I have been shipping iOS apps as an indie developer since 2014, and somewhere past fifty million combined downloads I started spending roughly as much time keeping up with Apple's policy changes as I did building features. Privacy Manifest (PrivacyInfo.xcprivacy), which Apple began requiring in stages from 2024, has been one of the heavier ones. The reason is structural: it is not enough that your own code is compliant. Every SDK you depend on also has to ship its own manifest, and you have to keep them all in sync as Apple's list of "commonly used APIs" expands.
A few months ago I bumped the AdMob SDK in Beautiful HD Wallpapers and was met with a rejection telling me that NSPrivacyAccessedAPITypes was missing entries. The same SDK update had gone through cleanly on one of my relaxing-music apps six months earlier. The difference was not obvious from a quick diff, and I lost a full day chasing it. This article distills what I have built since then so that days like that do not happen again. I use Claude Code for most of the heavy lifting, and I will show the prompts and scripts exactly as they sit in my repositories today.
Privacy Manifest is a declaration file Apple began rolling out as mandatory across spring 2024. Beyond your own app target, any SDK that touches Apple's list of "commonly used APIs" is also expected to ship its own manifest inside its framework bundle. There are three structural reasons this is harder for indie developers than for teams.
First, the rate at which SDKs adopt new requirements is inconsistent. A typical iOS app pulls in twenty or more dependencies — AdMob, Firebase, Sign in with Apple, lint helpers, reachability utilities — and indie developers rarely have a single dashboard that tracks the compliance status of all of them.
Second, dependencies that were fine when you added them can suddenly require a manifest later. Apple periodically expands the list of Required Reasons API categories. UserDefaults, fileTimestamp, systemBootTime, and diskSpace were all added after the initial release, and older indie apps tend to use them unconsciously.
Third, rejection messages are sparse. App Review usually says only "declarations for NSPrivacyAccessedAPITypes are missing or incomplete," leaving you to figure out which SDK is responsible and which API category needs a new entry.
Folding all of that into a monthly routine without an assistant is what burns time. The realistic move is to put the monitoring and patch generation on Claude Code.
The Minimum Vocabulary You Need
Just enough scaffolding so the rest of the article reads cleanly.
PrivacyInfo.xcprivacy declares four major keys. NSPrivacyTracking is a boolean for tracking activity. NSPrivacyTrackingDomains lists endpoints that contribute to tracking. NSPrivacyCollectedDataTypes describes the categories of data the app collects. And NSPrivacyAccessedAPITypes — the protagonist of this article — is an array of reasons for every Required Reasons API category your app uses.
The currently designated categories include File timestamp APIs, System boot time APIs, Disk space APIs, Active keyboard APIs, and User defaults APIs. Each category has a fixed set of reason codes — for instance, User Defaults APIs recognizes CA92.1, 1C8F.1, C56D.1, and AC6B.1. You pick the ones that match your actual usage and list them under each category.
The important point is that the values inside NSPrivacyAccessedAPITypeReasons are reason codes, not free text. When you have Claude Code generate manifest snippets, include the Apple reference list in your prompt so it does not invent codes.
✦
Thank you for reading this far.
Continue Reading
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦A pragmatic mental model of how Privacy Manifest and Required Reasons APIs interact, sized for indie iOS work rather than enterprise compliance teams
✦A Claude Code workflow with real commands for spotting drift when adding or updating dependencies, before App Store rejection
✦An end-to-end pattern that turns an App Store Connect rejection log into a unified-diff patch and re-submission
Secure payment via Stripe · Cancel anytime
✦
Unlock This Article
Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.
Across four long-running apps, almost every Privacy Manifest miss falls into one of four buckets.
A new dependency ships without its own PrivacyInfo.xcprivacy. Pods or SPM packages occasionally have a manifest only on a tagged release while the main branch lags, or vice versa.
An existing dependency expands its required reasons in a minor version bump. AdMob in particular has tracked Apple's expansions aggressively, which means your own app-level manifest needs to expand in lockstep.
Your own code starts using a Required Reasons API. You add a small UserDefaults.standard write for a settings toggle and forget to declare NSPrivacyAccessedAPICategoryUserDefaults.
A Swift Package's manifest is not in resources. If you publish a Swift Package as an SDK, the Package.swiftresources list must include PrivacyInfo.xcprivacy, or the framework artifact will not bundle it.
These four are easy to miss in code review and tend to surface only at submission time. Conversely, automating these four checks in CI tends to catch most of what would otherwise come back as a rejection.
Drafting Manifests with Claude Code
When you let Claude Code touch Privacy Manifest, give it a reference file at the root of the repo — I use docs/privacy-manifest-context.md — and load it with everything Apple-side that does not live in source. My version has four sections: a mapping of categories to reason codes, an inventory of UserDefaults keys with their purposes, a list of dependencies and the manifest paths they currently ship, and a history of past rejections with the fixes that resolved them.
With that context in place, the prompt I use for initial drafts looks like this.
# Run from the repository rootclaude << 'PROMPT'Read docs/privacy-manifest-context.md and the current PrivacyInfo.xcprivacy,then produce an update proposal under these constraints.1. Among the Required Reasons API categories my own code touches (User Defaults, File timestamp, Disk space, System boot time), list the ones that are not yet declared in xcprivacy.2. For each such category, propose 2-3 candidate reason codes drawn strictly from Apple's current reference. Pick one and justify it in a single line.3. Inspect Podfile.lock and Package.resolved. Flag dependencies whose bundled xcprivacy implies my app-level manifest needs additions.Return the result as a unified diff against PrivacyInfo.xcprivacy.PROMPT
The trick here is that the output is requested as a unified diff up front. That makes the patch trivially auditable and trivially applicable with patch -p0 or via Claude Code's Edit tool. Manifests punish typos: one broken character can derail a submission. Free-form prose responses are not worth that risk.
Equally important is treating any "recommended reason code" Claude Code suggests as a candidate rather than a fact, and verifying it against Apple's current reference. Apple updates these categories a few times per year, and a stale reference file will produce confidently wrong answers.
Funnelling Dependency Manifests into a Single Check
You do not need to merge dependency manifests into your own PrivacyInfo.xcprivacy. Xcode will collate them at build time from each Pod or framework bundle. In practice though, you will eventually meet an SDK that simply does not ship a manifest, or that loses it in a packaging step. A small coverage script catches most of this and is straightforward to have Claude Code generate.
#!/usr/bin/env bash# scripts/check-privacy-manifest-coverage.shset -euPROJECT_DIR="${1:-$PWD}"TARGET_PODS_DIR="${PROJECT_DIR}/Pods"echo "==> Checking PrivacyInfo.xcprivacy coverage under ${TARGET_PODS_DIR}"MISSING=()if [ -d "${TARGET_PODS_DIR}" ]; then while IFS= read -r pod_dir; do pod_name=$(basename "${pod_dir}") if ! find "${pod_dir}" -name "PrivacyInfo.xcprivacy" -type f \ -print -quit | grep -q .; then MISSING+=("${pod_name}") fi done < <(find "${TARGET_PODS_DIR}" -mindepth 1 -maxdepth 1 -type d)fiif [ "${#MISSING[@]}" -eq 0 ]; then echo "OK: every Pod ships a PrivacyInfo.xcprivacy" exit 0fiprintf "WARN: %d pods without PrivacyInfo.xcprivacy\n" "${#MISSING[@]}"printf ' - %s\n' "${MISSING[@]}"exit 1
Wire that script into the front of your Fastlane lane or a Makefile target so CI fails on any WARN. When CI catches a missing manifest, the temporary fix is to have Claude Code synthesize an equivalent xcprivacy and drop it at Pods/<name>/PrivacyInfo.xcprivacy. That is a stopgap, not a real fix — the real fix is the SDK author shipping one — but it has gotten me out of submission jams during peak release windows. On one relaxing-music app I depended on this stopgap for nearly three weeks while waiting for upstream, and it was the difference between staying on the release train and pulling the update entirely.
Wiring Manifest Checks into CI
The point of CI here is to remove the manifest from your daily mental load. My pipeline on GitHub Actions looks like this.
scripts/scan-required-reasons.sh is a thin grep -rn wrapper that detects calls into Required Reasons API surfaces, and diff-privacy-manifest.py compares the declared categories in PrivacyInfo.xcprivacy against the inventory file. Both are short enough (under 100 lines each) that Claude Code can produce a working first pass when given the repo's CLAUDE.md.
After rolling this out across the four indie apps I run, the average number of review round-trips driven by Privacy Manifest issues dropped from roughly two per month to between zero and one. The months when it lands at zero are worth maybe half a working day each — meaningful when you are also doing the design, the marketing, and the bookkeeping.
Turning a Rejection Log into a Patch
Rejections still happen. The English review messages from Apple often lack specifics, so the manual triage cost is real. The version of this work I have settled on hands the rejection straight to Claude Code and gets back a candidate patch.
# 1. Copy the rejection text from App Store Connect to the clipboard.# 2. Open the affected repository and start Claude Code.# 3. Run the prompt below.claude << 'PROMPT'Read the clipboard contents (the App Store Review rejection message),plus the current PrivacyInfo.xcprivacy, Podfile.lock, Package.resolved,and docs/required-reasons-inventory.json.Then do the following.1. Summarize the rejection reason in one sentence and state what is missing.2. Produce a unified-diff patch against PrivacyInfo.xcprivacy that fixes it.3. Propose an addition to required-reasons-inventory.json so that the same rejection does not recur silently.4. Judge whether this can be patched in our app, or whether it requires an upstream SDK update.Do not open a PR. Output only.PROMPT
The "do not open a PR" line is deliberate. With re-submission deadlines pressing, it is tempting to let the agent push, but Apple penalizes manifests that over-declare almost as much as ones that under-declare. The model is reliably good at summarizing the rejection in English and at proposing a diff. It is right about half the time on whether the fix needs an upstream SDK update; you can improve that by feeding it the release notes of the SDK in question.
Operating Principles That Are Worth Keeping
To close, three principles that I have come to trust after twelve years.
First, treat PrivacyInfo.xcprivacy as code. It belongs in CODEOWNERS, deserves a code review, and any dependency-bump PR should carry the matching manifest delta. CI is what guarantees that.
Second, do not let an assistant grow the manifest speculatively. Every category Claude Code proposes should be traceable to a real call site in your inventory. My own rule is that anything without a corresponding entry in required-reasons-inventory.json cannot be merged.
Third, keep a docs/rejection-history.md and make sure Claude Code can read it. Patterns repeat, and once the assistant has seen the same kind of rejection a few times its first-pass suggestion gets noticeably faster and more accurate. On my apps the AdMob-related NSPrivacyAccessedAPITypeReasons rejection that bit me three times has not recurred since I started keeping that file.
Privacy Manifest is invisible to users and adds nothing on the feature axis. For indie developers, though, it is the kind of quiet area where one missed detail can stop a release entirely. With Claude Code holding most of the watch, the energy you save here is energy you keep for what you actually wanted to ship.
Thanks for reading.
Share
Thank You for Reading
Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.