DEV Community

Roberto Frontado for Dev Jam

Posted on • Edited on

6 4

XcodeGen — Collaboration Made Easy

Illustration from [https://github.com/yonaskolb/XcodeGen](https://github.com/yonaskolb/XcodeGen)

If you have ever worked in a team, I’m sure that many times, you’ve encountered conflicts with the .xcodeproj file while merging your code.

Solving this kind of conflict is not always easy, because the .xcodeproj file is not human — readable, especially if you are adding and removing lots of files, modifying your project configuration, or editing your schemes…

In the past, I used a simple solution to this problem, adding this line to the .gitAttribute file.



\*.pbxproj merge=union


Enter fullscreen mode Exit fullscreen mode

This solution merges both sides automatically, so you don’t get to see the conflict. It works most of the time if you are in a small team. However, it is definitely not bulletproof nor scalable.

Wouldn’t it be better if you had a more reliable tool to solve this kind of problem? Here, XcodeGen comes to the rescue.

What Is XcodeGen, and How Do You Use It?

XcodeGen is a command-line tool written in Swift. It generates your Xcode project using your folder structure and a project spec, which contains all the information necessary to generate a project, such as targets, schemes, settings.

First, you need to install XcodeGen. You can do this using homebrew:



brew install xcodegen


Enter fullscreen mode Exit fullscreen mode

There are other ways of installing XcodeGen. You can check them out in the official documentation

Next, you need to create a file called project.yml in the project root folder. This file will contain the project configuration.

XcodeGen supports YAML and JSON files, but in this article, we use YAML because it is considered to be more human-readable and is the standard format for configuration files.

name: XcodeGenSample
options:
bundleIdPrefix: com.frontado
deploymentTarget:
iOS: 13.0
targets:
XcodeGenSample:
type: application
platform: iOS
sources:
- path: XcodeGenSample

To generate the project, run the following command:



xcodegen


Enter fullscreen mode Exit fullscreen mode

As simple as that, you have now generated an Xcode project!

In projects, you also have a unit tests target. It can be added to the project.yml file as follows:

name: XcodeGenSample
options:
bundleIdPrefix: com.frontado
deploymentTarget:
iOS: 13.0
targets:
XcodeGenSample:
type: application
platform: iOS
sources:
- path: XcodeGenSample
scheme:
testTargets:
- XcodeGenSampleTests
XcodeGenSampleTests:
type: bundle.unit-test
platform: iOS
sources:
- path: XcodeGenSampleTests

Important: Next time you merge code, and it modifies the project configuration, you’ll need to generate your project again by running xcodegen.

That’s not all, though. Let’s make things more interesting.

Imagine you are building a modular application, with two 2 frameworks: one to handle the authentication and one to see your user profile. Your project.yml file will look like this:

name: XcodeGenSample
options:
bundleIdPrefix: com.frontado
deploymentTarget:
iOS: 13.0
targets:
XcodeGenSample:
type: application
platform: iOS
sources:
- path: XcodeGenSample
dependencies:
- target: Authentication
- target: Profile
scheme:
testTargets:
- XcodeGenSampleTests
XcodeGenSampleTests:
type: bundle.unit-test
platform: iOS
sources:
- path: XcodeGenSampleTests
Authentication:
type: framework
platform: iOS
sources:
- path: Subprojects/Authentication
scheme:
testTargets:
- AuthenticationTests
AuthenticationTests:
type: bundle.unit-test
platform: iOS
sources:
- path: Subprojects/AuthenticationTests
Profile:
type: framework
platform: iOS
sources:
- path: Subprojects/Profile
scheme:
testTargets:
- ProfileTests
ProfileTests:
type: bundle.unit-test
platform: iOS
sources:
- path: Subprojects/ProfileTests

As you can see, this file starts to grow in size, making it harder to wrap our heads around it. Thankfully, XcodeGen allows us to split our configuration into multiple files, so it easier to manage. For example:

name: XcodeGenSample
options:
bundleIdPrefix: com.frontado
deploymentTarget:
iOS: 13.0
targets:
XcodeGenSample:
type: application
platform: iOS
sources:
- path: XcodeGenSample
dependencies:
- target: Authentication
- target: Profile
scheme:
testTargets:
- XcodeGenSampleTests
XcodeGenSampleTests:
type: bundle.unit-test
platform: iOS
sources:
- path: XcodeGenSampleTests
include:
- path: Subspecs/authentication.yml
- path: Subspecs/profile.yml
name: Authentication
options:
bundleIdPrefix: com.frontado
deploymentTarget:
iOS: 13.0
targets:
Authentication:
type: framework
platform: iOS
sources:
- path: ../Subprojects/Authentication
scheme:
testTargets:
- AuthenticationTests
AuthenticationTests:
type: bundle.unit-test
platform: iOS
sources:
- path: ../Subprojects/AuthenticationTests
name: Profile
options:
bundleIdPrefix: com.frontado
deploymentTarget:
iOS: 13.0
targets:
Profile:
type: framework
platform: iOS
sources:
- path: ../Subprojects/Profile
scheme:
testTargets:
- ProfileTests
ProfileTests:
type: bundle.unit-test
platform: iOS
sources:
- path: ../Subprojects/ProfileTests

Now generate the Xcode project again, and you will see that everything is working.

I have placed all frameworks in a folder called Subprojects, and their configuration files in another folder called Subspecs. You can organize them however you want.

You can also add build scripts. This is how you can add a script to run SwiftLint:

name: XcodeGenSample
options:
bundleIdPrefix: com.frontado
deploymentTarget:
iOS: 13.0
targets:
XcodeGenSample:
type: application
platform: iOS
sources:
- path: XcodeGenSample
dependencies:
- target: Authentication
- target: Profile
scheme:
testTargets:
- XcodeGenSampleTests
preBuildScripts:
- name: SwiftLint
script: |
${PODS_ROOT}/SwiftLint/swiftlint --config .swiftlint.yml
XcodeGenSampleTests:
type: bundle.unit-test
platform: iOS
sources:
- path: XcodeGenSampleTests
include:
- path: Subspecs/authentication.yml
- path: Subspecs/profile.yml

Note: When working with git, remember to add these lines to the .gitignore file because you don’t want to commit those files anymore:



\## Ignore project files as we generate them with xcodegen ([https://github.com/yonaskolb/XcodeGen](https://github.com/yonaskolb/XcodeGen))  
\*.xcodeproj  
\*.xcworkspace


Enter fullscreen mode Exit fullscreen mode

You can find the source code used for this article here:

GitHub logo robertofrontado / xcodegen-sample

XcodeGen sample project

Conclusions

Implementing a tool such asXcodeGen can seem overwhelming at first because it changes how you configure your project. However, I can assure you that you will reap its benefits from day one — especially if you work in a modular application and a large team.

Moreover, another benefit is that since you need to specify the entire project configuration through code, you automatically document your project configuration.

While XcodeGen is my preferred tool, here are some alternatives you can try and see which one suits you best:

GitHub logo tuist / tuist

Tuist's CLI

GitHub logo igor-makarov / xcake

🍰 Describe Xcode projects in a human readable format and (re)generate one on demand.

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

Top comments (1)

Collapse
 
arco_vv profile image
Arco

Thanks! I learn a lot.

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay