Last Updated: December 26, 2018
·
7.281K
· eamonnmoloney

Merging Xcode project files...

All IDE's like to pollute our workspaces with their own brand of tracking files. Eclipse, intelliJ, xCode, appCode, etc. They're all pretty nasty when it comes to this however xCode really did everything it could to make our lives miserable.

So my Pro Tip... How to navigate the world of merging xCode project files.


First is the easy stuff (for gitignore see here )

Assuming your using cocoa pods (you really should be :) )
1. Use GIT (or any DVCS. Merging is just not recommended for VCS systems)
2. Add the *.xcworkspace directory to your gitignore file
3. Add the *.xcodeproj/xcuserdata directory to your gitignore file
4. Add all other files to GIT
5. Mark all schemes that are not generated locally as "shared" (If your using cocoapods you will end generating new scheme locally)

Now the interesting stuff

The problem
All project information is contained within a huge file named project.pbxproject. You can find this in the xcodeproj directory. Assuming you're on a team of 5 - 6 developers, everyone is adding classes, setting compiler flag, adding resources, etc. and every one of these changes ends up in the pbxproject file.
This is huge problem for a development team as it mean everyone is editing this one huge file. (Imagine everyone working on one massive class).
To make things worse, this is not a particularly human friendly file so digging in and trying to manually resolve merge conflict is not all that simple without some.

The Issues
pbxproject background
Its an old style plist taken from Nexus (Thats right Steve Jobs company). Apple is keeping the format of the file private however a quick google provide glimpses of what the file means.

  1. Issue 1 (New classes added to the project) Adding 2 classes "ClassE" and "ClassF" will give you a merge conflict that cannot be handled automatically

code

<<<<<<< HEAD
    F73BAAD4166D710400F255CD /* ClassF.m in Sources */ = {isa = PBXBuildFile; fileRef = F73BAAD3166D710400F255CD /* ClassF.m */; };
=======
    F73BAAD0166D70F400F255CD /* ClassE.m in Sources */ = {isa = PBXBuildFile; fileRef = F73BAACF166D70F400F255CD /* ClassE.m */; };
>>>>>>> 501df570c086e7cacf3847270d7f197339e5595f

This snippet is just one of 4 conflict that are produced by 2 developers both adding a single class to the project.
Many panic when they see this as the number looks random. However this is actually very easy to resolve. The numbers in this sample are just a UUID. This means these is guaranteed to be globally unique (i.e. they will never be the same). It doesn't matter how many people are working on the project. It doesn't matter how many classes are added, these numbers are always unique.
What this means is that for this conflict you can safely remove the GIT merge markers and this conflict will be resolved.

  1. Issue 2 (Real conflicts) Most real conflict is actually very easy to resolve. Take the example below

code

<<<<<<< HEAD
    IPHONEOS_DEPLOYMENT_TARGET = 6.0;
=======
    IPHONEOS_DEPLOYMENT_TARGET = 4.3;
>>>>>>> 92e40f741eb04a04bd86985065efde22a709ee1e

There is no mystery here, my current HEAD branch wants to set the deployment target to 6.0 while a teammate wants 4.3... This is a genuine conflict and easy to resolve. ( Talk to each other :) )

  1. Issue 3 (Edge cases) The only real edge case I seen trouble with is when 2 teammates create a file with the same name. This can cause confusion as the only different between the 2 entries in this project file will be the UUID number and this is not easy to track in a big file with many changes. For such cases, just staying consistent should work as resolution. By this, I mean be sure to keep either the HEAD change of the external change, never mixup the 2 as the UUID number must stay consistent for Xcode to see the files. If you do not apply this kind of change consistently you will see a file in the "Build Phases -> Compile Sources" with a blank icon. The easy fix is to remove with file and add it back to the project.

All the best