Privacy Manifest Lint - xcprivacy & ITMS-91053

Checks PrivacyInfo.xcprivacy and your C#, Dart, JS, Swift and Kotlin source for Apple required-reason APIs, and decodes all 17 reason codes back to the category they belong to.

Find a tool

Get the complete version $29

This page is the working piece. The full pack has everything below.

Checks PrivacyInfo.xcprivacy and your C#, Dart, JS, Swift and Kotlin source for Apple required-reason APIs, and decodes all 17 reason codes back to the category they belong to.

An experienced freelance iOS developer bills $85-145/hour in 2026.

Buy the full version — $29
Want the full version?
Enter your email and we send the download link.

· ReadyStack

Worked example

Real numbers from this tool, line by line.

Privacy Manifest Lint - xcprivacy & ITMS-91053

Five defects come out of the nine-line PrivacyInfo.xcprivacy below - the kind a .NET MAUI or Flutter team ships without noticing, because Xcode never reads it back to them.

<key>NSPrivacyAccessedAPICategory</key>
<string>NSPrivacyAccessedAPICategoryUserDefault</string>
<key>NSPrivacyAccessedAPIReasons</key>
<array>
	<string>CA92.2</string>
	<string>AC6B.1</string>
</array>

Line 1: the key is NSPrivacyAccessedAPIType. The category name is its string value, not the key itself. Line 2: the category is plural - NSPrivacyAccessedAPICategoryUserDefaults. Line 3: the key is NSPrivacyAccessedAPITypeReasons. The word "Type" is missing. Line 5: CA92.2 is not a code Apple issues. Exactly 17 exist and every one of them ends in .1. Line 6: AC6B.1 is real, but it is only for MDM managed app configuration - reading com.apple.configuration.managed or writing com.apple.feedback.managed. The code for a third-party SDK that wraps the user-defaults APIs is C56D.1.

That last one is the expensive line, and it is the reason a language model is a bad place to get these. Ask a chatbot which reason code covers an SDK wrapper around NSUserDefaults and AC6B.1 is a very plausible answer. It is also wrong, it looks correct in the file, and nothing on your machine tells you otherwise. App Store Connect tells you, with ITMS-91055 or ITMS-91056, after the build, on the day you were going to release.

The second half of the problem is where the call actually lives. Apple's tooling inspects Swift and Objective-C. A .NET MAUI app reaches user defaults through Preferences.Default, reads the boot clock through Environment.TickCount, and calls stat through File.GetLastWriteTime. Flutter does it through shared_preferences and File.lastModifiedSync. React Native does it through react-native-device-info. None of those lines are Swift, so none of them show up in the place you are looking.

Privacy Manifest Lint is a VS Code extension and a free web page that read both halves. Open a PrivacyInfo.xcprivacy and it decodes every reason code in it back to the category Apple assigns it to, quoting Apple's own condition on that line, and flags any code Apple never issued. Open a .cs, .dart, .ts, .swift, .m or .kt file and it names the required-reason API you called, the category to declare, and the codes you are allowed to choose from. It ships 48 rules and all 17 reason codes, and it runs entirely on your machine - the file is never uploaded anywhere.

The free version checks one open file completely - every rule, every code, no watermark, no trial clock. The paid version changes the scope rather than the quality: the whole repository instead of one file, the report exported as CSV, JSON or HTML, CI JSON so a pipeline fails before App Store Connect does, and an in-place fix for the misspelled keys.

Paste your manifest into the free page first. If it comes back clean, you have lost nothing but a minute. If it comes back with five lines, you have just kept a release date.

15 seconds — what it actually does

Questions people ask

What does Privacy Manifest Lint actually do?

It reads a PrivacyInfo.xcprivacy file, or a source file, and reports two things. In the manifest it decodes every required-reason code back to the category Apple assigns it to, and flags codes Apple never issued. In source it finds required-reason API call sites in C#, Dart, JavaScript, TypeScript, Swift, Objective-C and Kotlin, and names the category and the codes you may declare.

Who is this for?

Teams shipping iOS apps built with .NET MAUI, Flutter, React Native, Xamarin or Capacitor. Xcode inspects Swift and Objective-C, so cross-platform code is where required-reason API use hides. If your upload came back with ITMS-91053, ITMS-91055, ITMS-91056 or ITMS-91061, this is the file-level check that finds the cause.

Why not just ask a chatbot for the reason code?

Because the codes are opaque four-character strings and a language model produces plausible ones that do not exist. AC6B.1 is a real code, but it is only for MDM managed app configuration; the third-party SDK wrapper code is C56D.1. A wrong pairing looks correct in the file and is rejected at upload, after the build.

What is free and what does the paid version add?

The free version checks one open file completely, with every rule and every reason code, and the free web page does the same in a browser. The paid version changes the scope, not the quality: it scans the whole repository, exports the report as CSV, JSON or HTML, writes CI JSON so a pipeline can fail early, and fixes misspelled manifest keys in place.

What does the alternative cost?

An experienced freelance iOS developer bills 85 to 145 US dollars an hour in 2026, so a single afternoon of hunting reason codes and resubmitting costs several hundred dollars. A rejected upload also costs a review cycle, which is the part that moves a release date. The full version is 29 dollars once, with a seven-day full refund.

Ask about this tool

One question, answered by the person who built it. Your email only if you want the answer sent.