How do you validate, read and convert an Info.plist or entitlements file from the command line, and what are the two plist formats?

6.11.4 · L2 · Interview

Predict first

What do plutil lint, extract and convert report?

// === Info.plist ===
<?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>
<key>CFBundleIdentifier</key><string>com.example.demo</string>
<key>CFBundleVersion</key><string>42</string>
<key>UIBackgroundModes</key><array><string>audio</string><string>fetch</string></array>
<key>NSCameraUsageDescription</key><string>To scan receipts</string>
</dict></plist>
// === Broken.plist ===
<?xml version="1.0" encoding="UTF-8"?>
<plist version="1.0"><dict><key>CFBundleVersion</key><string>42</dict></plist>
// === run.sh ===
plutil -lint Info.plist
plutil -lint Broken.plist 2>&1 | sed 's/^Broken.plist: //' | cut -c1-60
echo "bundle id:       $(plutil -extract CFBundleIdentifier raw Info.plist)"
echo "second mode:     $(plutil -extract UIBackgroundModes.1 raw Info.plist)"
echo "modes count:     $(plutil -extract UIBackgroundModes raw Info.plist)"
plutil -convert binary1 -o Info.bin Info.plist
echo "binary header:   $(head -c 6 Info.bin)"
echo "same data:       $(plutil -extract CFBundleVersion raw Info.bin)"
plutil -extract MissingKey raw Info.plist 2>&1 | sed 's/^.*: //' | head -1
  • The good plist is OK and the broken one reports a parse problem; extract reads dotted paths and array counts; the binary form starts with bplist and holds the same data; a missing key is an error message
  • Both plists are OK
  • Converting to binary loses the values

Mental model. XML and binary plists are the same recipe written in longhand and in shorthand. plutil is the translator that also proofreads.

A property list is either XML (human-readable and diff-friendly) or binary (compact and fast to parse, starting with the bytes bplist00); both hold the same data, and the system reads either. plutil handles both: plutil -lint file validates (a plist missing a closing tag fails with a parse error, which the check shows), plutil -extract key.path raw file prints one value using dotted paths with array indexes (UIBackgroundModes.1; extracting an array with raw prints its count), plutil -convert binary1 -o out file and -convert xml1 switch formats, plutil -p pretty-prints, and plutil -replace / -insert edit in place. Missing keys are a non-zero exit, which makes it safe in build scripts and CI: assert that a required usage description or the expected scheme is present before shipping.

Where this matters: Xcode converts the project’s Info.plist to binary in the built app, so reading the built file with a text editor shows gibberish, but plutil -p App.app/Info.plist is clear. Build settings expand $(VARIABLE) placeholders at build time, so a source plist can contain placeholders that a built one does not. Entitlements are plists as well (codesign -d --entitlements :- App.app prints the XML). A cheap CI check for the usual outages is to lint the plist, confirm that every UIBackgroundModes value is one you really use, that each NS…UsageDescription exists for the frameworks you link, and that CFBundleVersion increases (Q229, Q210). The check ran plutil on macOS against hand-written files; Xcode’s generated Info.plist settings (GENERATE_INFOPLIST_FILE) were not exercised.

plutil validates syntax and reads either format; conversion preserves content.

Common traps

  • Editing a built app’s binary plist with a text editor. It also invalidates the signature; change the source and rebuild.
  • Trusting that a valid plist is a correct one. -lint checks syntax only; check required keys explicitly.

They might follow up with

  • How do I show a plist in a diff? Keep the source in XML, or convert with plutil -p before comparing.
  • Can I edit entitlements the same way? Yes, they are plists; Xcode merges them from capabilities at build time.

Related questions

  • 6.7.1 What is Info.plist for, and which keys matter most?
  • 6.1.4 How do privacy permissions work, and what do you do after the user says no?
  • 6.1.3 What are entitlements, and what happens when they do not match the provisioning profile?
  • 6.11.3 How do you inspect what a binary links, what load commands it has and how it is signed with command-line…

← How do you inspect what a binary links, what load commands it has and…