Xcode 27 silently drops your camera usage string
In September I stashed some work and saw a change to Info.plist that I hadn't made. The keys had been re-sorted alphabetically, which I didn't care about. NSCameraUsageDescription was gone, which I cared about a lot.
The Lineup Is uses the camera for one thing: scanning a QR code to pair the app with a physical display. That flow calls AVCaptureDevice.requestAccess(for: .video). Without a camera usage string, iOS terminates the app on that call. App Review rejects the build too. Nothing in the build or the test suite fails when the key goes missing, so the first person to find out is whoever taps Scan.
What happened
Xcode 27 rewrites the source Info.plist for the app target. It re-sorts the keys and drops NSCameraUsageDescription. I first assumed it was a one-off from opening the project in the new Xcode, so I restored the file with git checkout and moved on.
It came back the same day. This time I hadn't opened the project at all. Plain command-line xcodebuild runs had done it. So this isn't something you trip once during an upgrade. Any build can do it, and it doesn't announce itself.
I don't know why Xcode decides that key can go. I only know that it does, and that the deletion is silent.
Why it's easy to miss
- The build succeeds. A missing usage string isn't a build error.
- Tests pass. Unless a test actually requests camera access on a device, nothing touches the key.
- The diff is noisy. When a whole plist gets re-sorted, a single deleted key hides among a lot of moved lines.
- It only shows up at runtime, on the one screen that needs the camera.
I caught it only because a stash showed a file I hadn't edited. A habit of committing everything that changed would have carried the deletion along with my real work.
Check the built Info.plist, not the source
The source file is what Xcode edits. The built file is what ships. Those are not the same thing, and the built one is the only one that matters to iOS and App Review. After a build, read the key straight out of the app bundle:
/usr/libexec/PlistBuddy -c "Print :NSCameraUsageDescription" \
"path/to/YourApp.app/Info.plist"
If it prints your string, you're fine. If it prints Print: Entry, ":NSCameraUsageDescription", Does Not Exist, the build you're about to ship will crash the moment it asks for the camera.
You can find the built app under your DerivedData Build/Products folder, or in the archive under Products/Applications. The same check works for any other usage string you depend on: microphone, location, photo library.
The fix: put the key in build settings
Modern Xcode projects can generate the Info.plist from build settings. With GENERATE_INFOPLIST_FILE = YES, any setting named INFOPLIST_KEY_<Key> becomes a key in the built plist, merged with whatever your source Info.plist still contains.
So I moved the camera string out of the file Xcode keeps rewriting and into the target's build settings, for both Debug and Release:
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = Info.plist;
INFOPLIST_KEY_NSCameraUsageDescription = "Scan the QR code shown on your display to pair it.";
In the Xcode UI this is the target's Build Settings tab, under Info.plist Values. Set it at the target level and check both configurations. A key that exists only in Debug will pass every local test and still ship without it.
Then I verified it the hard way. I built from a tree where Xcode had already stripped the key from the source Info.plist, ran the PlistBuddy check on the built app, and it printed the string. The source file can lose the key now and the shipped app still has it.
What I do now
- Every privacy usage string lives in build settings, not only in the source plist.
- I don't trust a clean source diff as proof the key is there. I check the built plist.
- When I see a plist change I didn't make, I read it line by line instead of discarding it as Xcode noise. Re-ordering is harmless. A missing key is not.
The fix is one build setting. The real risk was that nothing would have told me. If your app asks for any permission, run the PlistBuddy check on your next Xcode 27 build before you upload it.
I build The Lineup Is, an iPhone app that tells you which games are worth watching.
Get The Lineup Is on iOS — free — or see tonight's best games ranked right now. More from the blog.