How many times when working on a Mac OSX or iOS app with a team have you had a merge conflict on the project.pbxproj file? I guess more than a few, a lot more than a few.

In this article we are going to show a way to reduce the chances of such issues using a tool called xUnique.

Xcode's project file

Just in case you're not familiar with it, the infamous project.pbxproj is the way Xcode tracks which files are in the project, and describes the project settings, like targets and build configurations.

At the core of the way Xcode keeps track of all these things is the fact that every entry has it's own unique identifier.

You can read more about how the Xcode's project.pbxproj work in the Apple documentation, this old post on mokacoding, and this more recent one from Michele Titolo.

When working on a team it can happen, and because of Murphy's law it will, that some crazy edits to the project, like adding a file, made by two team members on two different commits will result in a nasty merge conflict on the project.pbxproj .

This happens because the unique identifiers used by Xcode are not that unique, and some changes to the project may result in a partial re-generation of the project file itself.

Enter xUnique

xUnique is a tool made by Sean Wang that makes sure the unique identifiers used in the project.pbxproj are actually unique.

It also sorts the project files for you, which is quite handy in my opinion.

Install xUnique

xUniqe is a Python tool, a fact rather unusual for iOS/OSX utilities.

The best way to install it is through PyPi.

pip install xUniqe

Before doing that you might want to make sure that your Python toolchain is up to date. I'd reccomend using homebrew for that.

Uniquifying the project

With xUnique now installed you can "uniquify" the project.pbxproj like this:

python -mxUnique MyAwesomeApp . xcodeproj/project.pbxproj

The first thing you'll see is that all your groups and files in Xcode have been sorted alphabetically, and if you'll look at the git diff you'll see that all the identifiers in the project file have changed.

And example diff can be seen here, in the example project we setup for the occasion.

Automating the process

You might now be saying: "But I don't want to run that command every time I touch the project!" . And you'd be right!

Everything that can be automated should be automated

We can easily automate this process by adding a Run Script Build Phase to our target and have it run xUnique for as after every build.

I like to have my run scripts in their own file rather than in the Run Script text view, it makes it easier to edit them with your text editor of choice.

python -mxUnique " ${PROJECT_FILE_PATH} /project.pbxproj"

The version of the script above uses the PROJECT_FILE_PATH environmet variable provided by Xcode to the Run Scripts. If you want to be able to run the script yourself replace it with MyAwesomeApp.xcodeproj .

Something to keep in mind

While once automated this process is almost frictionless, there are some things to keep in mind:

Every time the project is touched by xUnique, the state of the Project Navigator resets.

All the users of the project will have to install xUnique, or their build will fail.

xUnique replaces Xcode's identifiers with MD5 ones. Up till now this hasn't been a problem, but what would happen if Xcode stopped understanding IDs in such format?

The tool will run for every build, but if a developer were to change the project and commit without building then project.pbxproj changes wouldn't be uniquified.

In the next post we'll see how to make sure xUnique always run, even someone doesn't build, through a git pre-commit hook.

Xcode has still a long way to go before becoming the IDE Mac and iOS developer deserve, but it's getting closer every point update. As users we can cope with some of it's limitations using some of the excellent plugings that the OSS community has realised, checkout Alcatraz for a starting point, open radars, and sometimes give it some extra help with scripts like xUnique.

I hope of have triggered your curiosity with this post, as usual there is an example repo that you can use to see the full code, and I'd be happy to help you setup xUnique if you need extra help, just tweet @mokagio.