An Xcode .gitignore ignores per-user state (xcuserdata/, *.xcuserstate), build output (build/, DerivedData/, and .build/ from Swift Package Manager), packaged apps and debug symbols (*.ipa, *.dSYM), and CocoaPods’ Pods/. Those are the key lines of the tool’s Swift & Xcode template, which has 30.
What the blocks ignore
- Per-user state:
xcuserdata/at any depth, inside the project and the workspace, holding window layout, breakpoints and unshared schemes. - Build output:
build/,DerivedData/,.build/, and*.hmap. - Packaging:
*.ipa,*.dSYMand*.dSYM.zip. - Swift packages:
.swiftpm/configuration/registries.jsonand Xcode’s generatedpackage.xcworkspacefile, not the whole.swiftpm/folder. - Dependencies and tooling:
Pods/,Carthage/Build/, and fastlane’s reports and screenshots.
In a scratch repository with this file, git check-ignore -v -n --no-index names the line that matched each path, or prints :: when none did:
:: MyApp.xcodeproj/project.pbxproj
:: MyApp.xcodeproj/xcshareddata/xcschemes/MyApp.xcscheme
:: MyApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
.gitignore:3:xcuserdata/ MyApp.xcodeproj/xcuserdata/me.xcuserdatad/xcschemes/xcschememanagement.plist
.gitignore:8:DerivedData/ DerivedData/x
:: .swiftpm/xcode/xcshareddata/xcschemes/MyLib.xcscheme
:: Podfile.lock
.gitignore:31:Pods/ Pods/Alamofire/x
Never ignore xcshareddata
project.pbxproj is the project itself. Everything under xcshareddata/ is shared on purpose: schemes you marked Shared, which CI needs for xcodebuild -scheme, and swiftpm/Package.resolved, the lockfile for your Swift packages. A blanket *.xcodeproj/* or .swiftpm/ line hides them. Commit Podfile.lock too, so pod install restores the same Pods/.
build/ and a folder called Build
Git on a Mac usually runs with core.ignorecase on, so build/ also matches a source group folder named Build. On Linux CI it doesn’t, and the two machines disagree:
:: Sources/MyLib/Build/Helper.swift
.gitignore:7:build/ Sources/MyLib/Build/Helper.swift
The first line is core.ignorecase=false, the second true. Rename the folder, or anchor the line as /build/.
How the !default lines work
The template pairs each Xcode 3 per-user format with its shared default, such as *.pbxuser and !default.pbxuser. The ! line comes second, because the last match wins, and it works because neither file sits in an excluded folder:
.gitignore:11:*.pbxuser me.pbxuser
.gitignore:12:!default.pbxuser default.pbxuser
Inside an excluded folder, ! does nothing: Git never looks into xcuserdata/, so a line re-including xcuserdata/me.xcuserdatad/xcschemes/MyApp.xcscheme still leaves it reported against line 3. Tick Shared for it under Product > Scheme > Manage Schemes instead, and Xcode moves the scheme into xcshareddata/.
Committed xcuserdata already?
.gitignore only affects untracked files. Untrack it, keeping it on disk, and commit:
git rm -r --cached MyApp.xcodeproj/xcuserdata MyApp.xcodeproj/project.xcworkspace/xcuserdata
git commit -m "Stop tracking per-user Xcode state"
.DS_Store
This template doesn’t ignore .DS_Store; add the macOS template, or ignore it once for every repository:
git config --global core.excludesFile ~/.gitignore_global