Avoiding merge conflicts with XcodeGen
I - Intro
This article provides a solution to instantly solve merge conflicts on .xcodeproj file, which is one of the most time consuming problems that iOS and macOS engineering teams face today.
By the end of this article you’ll have a working environment that provides a seamless merge experience.
II — Before we start
1. Introduction XcodeGen
XcodeGen is a command line tool written in Swift that generates your Xcode project using your folder structure and a…
XcodeGen is a command line tool written in Swift that generates your Xcode project using your folder structure and a project spec.
2. Use case project
Nowadays, most projects aren’t simple. They have multiple configurations, dependencies (provided by third parties or developed as part of the project), scripts that run as part of the build process, CI integrations, etc.
As we can see above, the project have:
III — Setting up
1. Set up sample project
The sample project that we’ll use on this walk through has carthage dependencies. Just run
makeProject.shon a terminal. This will setup the project for you with all its dependencies and we can start the tutorial.
2. Creating your XcodeGen project specs file
At the project root directory, create a folder named XcodeGen(folder name is optional) with a file named
project.yml (file name is optional).
During our inicial setup we will choose the .xcodeproj file name — GoodToGo — , the bundle prefix — com.GoodToGo — , Xcode version, deployment target…
3. First XcodeGen run
Using the above setup, we can create our new project running
xcodegen -s ./XcodeGen/project.yml -p ./ on the terminal.
./XcodeGen/project.yml is our project spec file path. If you choose other folder/file name you need to change this too.
Green message saying that project was created? We’ve successfully created our project!
Lets now open it and see how it looks:
Not what we were expecting… All of our schemes are missing, all the frameworks, everything. They are missing because we didn’t defined them on our
4. Adding configurations and project main target
Just append on your
Basically, we are creating 3 configurations, Debug.Dev, Debug.Prod, Release, and stating that our app main target will be named GoodToGo, targeted for iOS 12 (minimum version) and the source code files are on folder GoodToGo.
The folder must exist on system folders or else you’ll have the following error:
Spec validation error: Target “GoodToGo” has a missing source directory “/Users/ricardosantos/Desktop/GitHub/RJPS_Articles/7/sourcecode/THE_FOLDER_THAT_DOES_NOT_EXISTS_NAME”
Run again with our updated configuration (previous image) and…
…we have (preview image) Schemes, configurations, and app main target!
5. Adding frameworks
Next thing is to add our framework dependencies. For each one, we need to choose the platform, type, deployment target and other settings.
We know from the get go that, usually, all the frameworks share the same settings, so we will create a template and call it Framework.
Bellow, we have a simple template that we will refer to on our main
project.yml file and then reference it where needed.
This template states that all the targets will have this conditions:
- type framework,
- platform iOS,
- version number as 1.0,
- deployment target equal to, or above, iOS 11.0.
From our sample project, we had the following framework dependencies (see below).
Now, we’ll add 2 for a quick test: AppTheme and AppResources.
Starting from our
project.yml file, on targets section, we will add 2 new targets (AppTheme and AppResources). To these targets, we must define the source folder and the template name, as explained in the previous step.
Run again with our updated configuration (previous image) and…
…we now have our app main target and the 2 dependencies we just added on our spec file.
Small recap: our
project.yml on the target section should look like:
To complete the setup we need to add the rest of the frameworks, after which our
project.yml file should look like:
At this point, after executing XcodeGen, we should have the project with all the frameworks!
While setting up these kind of things, we always hit bumps on the road. One of those is spotting a missing .plist file reference. This can be fixed in 2 ways: one is to configure on the
.plist file path and the second option is to rename GoodToGo-Info.plist to Info.plist.
6. Fixing dependencies frameworks
At this point we have our basic project configuration, the schemes back and our frameworks back. It is time to fix our dependencies and compile our project successfully.
For instance, we noticed that Domain depends on RxCocoa and RxSwift, and these dependencies are resolved via carthage.
First, we need to find the Domain target on our
…and then add the missing (carthage) dependencies. As simple as that!
To add some dependencies we have several options, but on our sample project we just need 3:
- Add a carthage dependency,
- Add a project dependency and linking,
- Add a project dependency and don’t link.
You can learn more about adding dependencies here.
It’s time to fix all the dependencies on our project and we will be basically done! “Basically” is just a way of saying it, this task is probably the most time consuming one, its a cycle of:
- 1. Generating .xcodeproject and open it,
- 2. Compiling the project and identifying missing dependencies,
- 3. Adding missing dependencies on
- 4. Go back to Step 1.
7. Extra: Build fase scripts
On the original project, we had some script build phases (as we can see on the image below):
We can achieve the same result by adding a postCompileScript child.
8. Extra: Documents folder
We can add also a Documents folder. The files inside this folder will not be added to any target, and therefore will not be processed as part of the build. Just add the path of your documents folder to your fileGroups section (more about fileGroups here).
IV - Recap
- We created a basic
project.ymlfile and used XcodeGen to take it and generate our project (more info here)
- We added some project configurations (more info here)
- We added all targets we needed
- We fixed the targets’ dependencies (more info here)
- We added the documents folder (more info here)
- We added build phases scripts
1. Some problems you may face while setting up project the first time: Lost files
Happens to all of us, sometimes. While removing some old/deprecated files from the project, we choose the option to Remove Reference instead of moving the file to trash. These files will come back again when we use XcodeGen (remember that all files inside the folder will be added to the target). If you really want to keep these files, put them on the Documents folder.
2. Some problems you may face while setting up project the first time: Miscellaneous files inside folders
Sometimes we have miscellaneous files inside our targets’ folders. Again, remember that all the files inside a target folder will be add to the target and therefore compiled. You can avoid this by putting those files inside the Documents folder.
V - Materials
- XcodeGen can be found here.
- The project before the conversion can be found at here.
- The final project source code can be found here.
- The final project.yml can be found at here.
- More examples of similar projects can be found here.
Article review by
Charles Prado - Medium
About a year ago the iOS 13 was announced at WWDC19 and one of its most anticipated features was the Dark Mode. With…
Tiago Janela - Medium
In this series we will tackle the problem of optimizing network access to fetch data from the network, a common theme…
Enhancing XcodeGen for simpler maintenance of dependencies in modular iOS app
Breaking the monolith