Episode #541

Migrating to Xcodegen

31 minutes
Published on November 14, 2022

This video is only available to subscribers. Get access to this video and 541 others.

When working on a large project with multiple developers, we often find ourselves in contention with the Xcode project.pbxproj file. Conflicts here are not easy to solve manually, and if you make a mistake Xcode won’t load the project at all, requiring you to fix it manually. In this episode we will explore migrating to a solution where the project file is generated using a tool called Xcodegen.

When working on a large project with multiple developers, we often find ourselves in contention with the Xcode project.pbxproj file. Conflicts here are not easy to solve manually, and if you make a mistake Xcode won’t load the project at all, requiring you to fix it manually.

There are some solutions to mitigate these problems and one of them is to treat the Xcode project as a generated artifact instead of the source of truth.

Doing so changes a bit of your workflow, but the benefits are pretty obvious once you get used to it.

In this episode we’ll migrate our Wurdle project from a past series to use Xcodegen.

If you want to follow along, make sure to download the source for that episode first. The completed project.yml file will be provided in this episode's git repository.

Installing Xcodegen

We can install Xcodegen with Homebrew.

$ brew install xcodegen

Once installed, you will run xcodegen any time you want to regenerate a project. But in order to do that, we first need a project.yml file.

The Project YML file

The project.yml file contains all of the information needed to create your Xcode project. At first it may seem a little overwhelming, however it contains lots of sensible default behavior and you start to get used to the structure over time.

Be prepared to reference the Project Spec documentation regularly as you build it out.

Our project.yml contains some top-level information:

name: Wurdle
options:
  bundleIdPrefix: com.nsscreencast.wurdle

Adding Swift Packages

To specify which packages we may want to install, we add a packages section to the top level as well:

packages:
  Yams:
    url: https://github.com/jpsim/Yams
    from: 2.0.0

Note: We don’t actually need this package, but I wanted to show how this is done.

Configuring Targets

Your project is made up of targets. In this example, we’ll create two targets:

  • iOS Application
  • Framework

We’ll put our model code and any other code we may want to share or test separately in the framework. All of the iOS UI code will go in the application target.

targets:
  Wurdle:
    type: application
    platform: iOS
    deploymentTarget: "16.0"
    sources: [Sources/Wurdle]
    info:
      path: Sources/Wurdle/Info.plist
      properties:
        CFBundleDisplayName: WURDLE!
        UISupportedInterfaceOrientations: [UIInterfaceOrientationPortrait]
        UILaunchStoryboardName: LaunchScreen

    dependencies:
      - target: WurdleKit

  WurdleKit:
    type: framework
    platform: iOS
    sources: [Sources/WurdleKit]
    dependencies:
      - package: Yams
    info:
      path: Sources/WurdleKit/Info.plist
      properties:

In this example, we have indicated that we want an Info.plist file generated with the specified properties. You can also reference an existing Info.plist file if you have one already and don't want to migrate to a generated one.

At this point all source files that exist in the folder structure you specified will be picked up automatically.

Generating the project

Now run xcodegen to generate your project. You will run this often:

  • every time you pull new changes from the server
  • every time you add new files or configuration
  • every time you switch branches

Since we are continually generating this project file, it’s a good idea now to ignore it from the git repository entirely:

$ git rm -r --cached Wurdle.xcodeproj

(deletes the file from the git index)

$ echo "Wurdle.xcodeproj" >> .gitignore

$ git add . && git commit -m "Ignore xcode project"

This episode uses Xcode 14.0, Xcodegen 2.32.0.