Reading an IPA Property List: What an iOS App Declares About Itself
An iOS app package, an IPA file, contains more than code. It contains a property list, a structured file of keys and values, that declares what the app is, which version it is, what devices it supports, and what it is allowed to ask the user for. Developers read this file to confirm a build, testers read it to check they have the right version, and reviewers read it to understand what an app claims to do. The IPA Property List Viewer displays a property list you paste in as readable keys and values, in your browser, without running the app or contacting any server. It reads the text you supply; it cannot verify that the app was signed by a particular developer, and it cannot tell you whether the app is safe.
Property lists come in a few encodings, but the XML form is the one people read most easily: nested dictionaries, arrays, strings, numbers and booleans, each labelled by its key. The viewer turns that nesting into something scannable, so you can find a single key without wading through raw markup. That matters because a real plist can be long, and the key you need, such as the minimum operating system version, may sit deep inside a nested dictionary. Reading it in a viewer is faster and less error-prone than reading raw XML.
How it works
A handful of keys carry most of the meaning. CFBundleIdentifier is the app's unique identifier, in reverse-domain form, and two apps with the same identifier are treated as the same app by the system. CFBundleShortVersionString is the marketing version users see, such as 2.4.1, while CFBundleVersion is the build number, which increases with every build even when the marketing version does not. MinimumOSVersion states the oldest iOS version the app supports, and UIDeviceFamily says whether it targets phones, tablets or both. Permission keys, whose names begin with NS, carry the explanation text shown to users when the app requests camera, location or similar access; the text matters, because a vague explanation is a common reason users refuse permission.
Other keys describe capabilities and requirements: whether the app needs certain hardware features, which orientations it supports, and which URL schemes it claims. These are declarations, not guarantees. An app can declare support for a device and still behave badly on it, and an app can omit a permission description and then fail when it tries to use that feature. The plist tells you what the app says about itself, and testing tells you whether the claim holds.
Where it goes wrong
The most common reading error is confusing the marketing version with the build number, and then concluding that two builds are identical when only their marketing versions match. Another is assuming the bundle identifier is a website address; it looks like a reversed domain, but it is an identifier, and it may not correspond to any real domain. A third is treating a permission description as proof that the app actually uses that permission, when in fact apps sometimes declare permissions they never exercise, and sometimes exercise permissions whose descriptions are misleading. Finally, never paste a plist that contains secrets, tokens or private keys into any viewer or shared document; strip sensitive values first, since a plist can carry more than metadata.
Worked example
Worked example with sample values. Suppose you paste a sample plist containing 24 keys in total. The viewer shows 3 identity keys: bundle identifier com.example.sampleapp, marketing version 2.4.1 and build number 87. It shows 1 minimum version key, 15.0, and 2 device family entries, phone and tablet, which is 2 entries. It shows 4 permission descriptions: camera, photo library, location and notifications, which is 4. Adding the counted groups gives 3 plus 1 plus 2 plus 4, which is 10 keys accounted for in the summary, leaving 24 minus 10, which is 14 further keys for capabilities, orientations and other settings. The build arithmetic is also useful: if the previous build was 86 and this one is 87, the difference is 1, confirming a single new build rather than a jump. These are sample values for illustration, not a real app's metadata, and the counts show how a summary turns a long file into a checkable list.
Mistakes to avoid
One mistake is editing a plist by hand and repackaging an app without understanding code signing, which will break the app's signature and stop it installing. Another is copying a plist from one app to another and changing only the name, leaving the old bundle identifier behind, which creates an identity collision. A third is ignoring the minimum version key and then wondering why an app will not install on an older device. Read the plist before installing or distributing a build, and treat any surprise in it as a question for the developer, not a detail to ignore.
Putting it into practice
Make plist reading part of the build check. When a new build arrives, paste its plist into the viewer, confirm the bundle identifier, marketing version and build number against the release notes, and check the minimum version and permission descriptions against what the release claims. Keep the checked summary with the build record, so that a later question about "which build was that" can be answered from the record rather than from memory. That routine takes a minute and prevents the classic embarrassment of shipping the wrong build under the right name.
Related tools and reading: the Webcam Test and the Microphone Test cover neighbouring checks that pair naturally with this one, and the guide at [/blog/website-seo-audit-guide/] extends the same topic with further background and examples for readers who want to go deeper.
Key points
- The plist is the app's identity card, not its code
- Bundle ID plus version identifies a build uniquely in practice
- Permission descriptions explain why the app asks for access
- Never paste a plist containing secrets or private keys
In short: A property list declares an app's identity, version, requirements and permission explanations. Read the bundle identifier, marketing version and build number together, and never confuse the version users see with the build number underneath. The viewer reads pasted text; it does not verify signatures or safety.
One habit ties all of this together: re-check after every change. A result is only as current as the input it was run on, so a fix, a redesign, or a new version of the underlying material deserves a fresh run rather than reliance on an old report. Keep the previous result alongside the new one, note what changed between them, and treat any new finding as a question to investigate rather than a verdict to accept or dismiss. That loop of checking, fixing, re-checking and recording is what turns a one-off test into a reliable routine, and it costs far less time than debugging a problem that a second check would have caught early.