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.

Image from https://medium.com/@kautilyasave/xcode-merge-conflict-debugging-5904c7e0cc59

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 project spec.

We’ll use Brew to install XcodeGen. Just run on terminalbrew install xcodegen(more install options and instructions can be found here ).

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.

During this tutorial we will see how to setup, step by step, an example project that is simple enough to explain the gist of XcodeGen while being complex enough to cover a good range of case uses.

Sample project structure

As we can see above, the project have:

  • Framework dependencies
  • Carthage dependencies
  • Schemes and configurations
  • Build phases scripts

III — Setting up

Sample app usage screens

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.

Installing sample project (Brew, Carthage, XcodeGen, dependencies…)

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).

This file will be the spec of our .xcodeproj
XcodeGen/project.yml : Initial setup

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.

Notice that ./XcodeGen/project.yml is our project spec file path. If you choose other folder/file name you need to change this too.

XcodeGen/project.yml : first run

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 project.yml file.

4. Adding configurations and project main target

Just append on your project.yml file:

XcodeGen/project.yml : Adding configurations

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.

XcodeGen/project.yml : Creating a template

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.

XcodeGen/project.yml : Adding targets using template

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:

XcodeGen/project.yml : Small recap

To complete the setup we need to add the rest of the frameworks, after which our project.yml file should look like:

XcodeGen/project.yml : Complete setup (so far)
https://seattle.eater.com/2019/7/2/20679237/july-4th-seattle-where-to-eat-drink-and-watch-the-fireworks

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 project.yml 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 project.yml file…

XcodeGen/project.yml : Domain target without dependencies

…and then add the missing (carthage) dependencies. As simple as that!

XcodeGen/project.yml : Domain target with dependencies

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.
XcodeGen/project.yml : Example of the 3 dependencies options (carthage, linked and not linked)

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 project.yml,
  • 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.yml file 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

Related

--

--

--

https://github.com/ricardopsantos/ricardopsantos

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Technology Stack for Telemedicine App Development

IdeaSoft tech stack for telemedicine app development

Handling 2 protocols with same var / func name

Dynamic Type: Adaptable Layouts

Going from two classes to three with Skafos.ai and TuriCreate

Swift UI E-Book APP 作業 — KPOP E-Book

Capture iPhone iOS HTTP traffic Using Wireshark

Swift extension library to make development easier

Stretchy table header view (iOS)

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Ricardo Santos

Ricardo Santos

https://github.com/ricardopsantos/ricardopsantos

More from Medium

What is the difference between Struct and Class?

Singly Linked Lists in Swift

Adapter vs Wrapper

RxSwift Combination Operators: zip, combineLatest & withLatestFrom