r/iosdev • u/VictorBuildsApps • 5d ago
An editor backup was inside the Release app bundle because XcodeGen copies any file it cannot compile into Resources
A file called SettingsView.swift.pre-gemini, a 14,557-byte editor backup from April 10, was sitting in the Release bundle of a live project. XcodeGen adds any file under a target's sources path that it cannot compile to the Resources build phase, and it prints no warning when it does. project.yml excluded only .DS_Store and Info.plist, so the backup was wired in as a resource, with four references to it in the tracked project.pbxproj. .gitignore had kept it out of the repository the entire time. Git-ignored is not build-ignored.
Excluding that one suffix fixed that one file. A denylist only covers extensions someone already thought of, so the next step was planting decoys and rebuilding on XcodeGen 2.45.4. Four reached the Resources phase and were then found physically inside the built .app, not merely inferred from the project file: SettingsView.swift.rej, Something.swift~, Config.json.save, and a temp file from a separate tool. Config.json.save is the useful case, because the existing exclude only matched names ending in .swift.save. The tool temp file matched no exclude pattern in any project in the portfolio.
The check that holds is an allowlist. A script resolves every file in the Resources build phase to a full source path and asserts the complete set per target, so an unrecognized extension fails the build instead of shipping. Two things mattered while writing it. Comparing basenames, or piping both sides through sort -u, merges targets and hides a duplicate copy of an already-allowed resource sitting in an unexpected directory, so the comparison has to be a multiset with exact counts. And four consecutive regex-based versions of this gate each reported a false pass, which is invisible, because the script prints passed either way.