xcuserdata/, DerivedData/, .DS_Store, and build outputs. If user data is already tracked, purge it from the Git cache using git rm -r --cached . and commit the updated .gitignore.Why this happens
Xcode stores project configuration inside bundle directories such as .xcodeproj and .xcworkspace. Unlike standard directories, these bundles package both team-wide settings and machine-specific developer state in the same hierarchy. Every time you open a file, adjust a split pane, add a breakpoint, or switch the active build scheme, Xcode modifies files inside a subfolder named xcuserdata.
When a repository lacks an explicit xcode gitignore configuration, Git automatically stages these state updates. This causes recurring merge conflicts on XML metadata, clutters code reviews with hundreds of trivial changes, and risks leaking absolute file system paths or private developer credentials into commit logs.
In addition to project bundles, Xcode’s build system generates massive artifacts outside your source code. Folders like DerivedData, build products, and dependency caches should never live in version control. Storing them bloats repository size and leads to compiler mismatches across different developer environments.
How to fix it
1. Create a comprehensive .gitignore file
Create a .gitignore file at the root of your Git repository. This production template covers modern Xcode projects, Swift Package Manager, CocoaPods, Carthage, Fastlane, and macOS system clutter:
# =========================================
# Xcode User State and Project Artifacts
# =========================================
# User-specific settings (window layout, open files, breakpoints)
xcuserdata/
*.xcuserdatad/
*.xcuserstate
*.pbxuser
*.mode1v3
*.mode2v3
*.perspectivev3
# Xcode 5+ Source Control checkout metadata
*.xccheckout
*.xcscmblueprint
# Xcode workspace state
.idea/
# =========================================
# Build Outputs & Derived Data
# =========================================
# Compilation outputs, intermediate objects, and indexes
build/
DerivedData/
*.hmap
*.ipa
*.dSYM.zip
*.dSYM
# =========================================
# Swift Package Manager (SPM)
# =========================================
# SPM build products
.build/
Packages/
# Xcode SPM checkout artifacts
.swiftpm/xcode/xcuserdata/
# NOTE: For apps, commit Package.resolved to lock dependency versions.
# If you are developing a standalone library, you can uncomment the line below:
# Package.resolved
# =========================================
# Dependency Managers (CocoaPods & Carthage)
# =========================================
# CocoaPods: Ignore local pods and workspace overrides
Pods/
!Podfile.lock
# Carthage: Ignore dynamic framework build products
Carthage/Build/
Carthage/DerivedData/
# Fastlane: Ignore temporary test summaries and build reports
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots/**/*.png
!fastlane/screenshots/**/*.gitkeep
fastlane/test_output
# =========================================
# macOS System Files
# =========================================
.DS_Store
.AppleDouble
.LSOverride
Icon
._*
.Spotlight-V100
.Trashes
Save this file as .gitignore in your repository root.
2. Untrack already committed user files
If you committed files before adding the .gitignore, Git continues tracking them. Modifying .gitignore does not remove files that are already part of the Git index.
To strip tracked user files without deleting your local Xcode configurations, clear Git’s cache and re-index the repository:
# Navigate to your project directory
cd /path/to/your/project
# Stage any pending work or commit it first
git add .gitignore
git commit -m "Add comprehensive Xcode .gitignore"
# Remove all indexed files from Git's tracking tree
git rm -r --cached .
# Re-add all files (Git will now respect .gitignore rules)
git add .
# Commit the clean file list
git commit -m "Untrack cached Xcode user data and build artifacts"
Verify that files inside xcuserdata and DerivedData no longer show up under git status.
3. Share Xcode schemes across the team
By default, newly created Xcode schemes are marked as personal and stored in xcuserdata/. When you ignore xcuserdata, other developers will not see your scheme, causing build errors like The scheme 'MyApp' does not exist on continuous integration (CI) servers.
Convert critical schemes to shared status:
- In Xcode, click the active scheme in the toolbar and select Manage Schemes….
- In the scheme table, locate your target schemes.
- Check the Shared box on the far right column for each scheme.
- Click Close.
Xcode moves the scheme file from:
MyApp.xcodeproj/xcuserdata/<username>.xcuserdatad/xcschemes/MyApp.xcscheme
to:
MyApp.xcodeproj/xcshareddata/xcschemes/MyApp.xcscheme
Because xcshareddata is not in .gitignore, Git will track the shared scheme while keeping individual window states private.
How to prevent it
- Configure a global gitignore on your macOS workstation to prevent
.DS_Storeand personal tool files from leaking into any repository:
bash
git config --global core.excludesfile ~/.gitignore_global
echo ".DS_Store" >> ~/.gitignore_global
echo "xcuserdata/" >> ~/.gitignore_global - Check the Shared checkbox in Product > Scheme > Manage Schemes… immediately whenever you create a new target or test configuration.
- Review your staged files using
git statusor a GUI Git client before finalizing your first commit on a new project. - Add pre-commit hooks or CI lint checks to block any pull request attempting to add files inside
xcuserdataorDerivedData.
FAQ
Should I commit Package.resolved to version control?
For standalone iOS and macOS applications, yes. Committing Package.resolved pins external Swift package versions, guaranteeing that everyone on your team and your CI pipeline builds against the exact same dependency revisions. Only standalone Swift package libraries intended for distribution should exclude Package.resolved.
What is the difference between xcuserdata and xcshareddata?
xcuserdata holds private, transient session settings specific to one user (active document tabs, editor break points, personal window bounds). xcshareddata holds team-wide project configuration, such as shared build schemes and continuous integration configurations, which must be committed to Git.
Why does Git still detect changes to my project.pbxproj file?
project.pbxproj is the core project manifest that lists every file, target, build configuration, and build phase. It must remain tracked in Git so the team shares the same project structure. If you see unwanted diffs, ensure developers avoid constantly reordering files or re-saving identical build settings across different Xcode versions.
Parts of this guide are adapted from content licensed under CC BY-SA 3.0; this article is shared under the same license.
