Michele Titolo | Blog | Speaking

The Project File Part 1: Composition

The project file, specifically the project.pbxproj, is the closest thing to taboo we deal with on a regular basis as iOS developers. We tip toe around it, because we don’t want to break it. After all, without this file we can’t compile, and if we can’t compile, we can’t build apps.

Object Graph

At it’s heart, this file is an object graph. All of these objects correspond with some sort of action you do in Xcode. This is the master list of files, target composition, build settings, etc. Each item is referenced by a UID.

One of the most important things to learn about the project file is that these UIDs must not be changed. They need to be consistent within the file, otherwise Xcode cannot open your project. Conversely, if there are extra, unused references, Xcode doesn’t care.

Traversing the graph is pretty simple. It’s just a matter of picking a UID and finding it elsewhere in the document. There are a number of intermediary objects to go through to find a particular target that contains a particular file, the files in a build phase etc.

Classes

As you scan through this file, you’ll note that every object has an isa field. These names (likely) correspond with the actual objects created when you open the file. For the purpose of this exploration, I’ll be referring to these objects as Classes. And, for the most part, they are all prefixed with PBX or XC for consistency. Here is the full list as of Xcode 6:

PBXAggregateTarget
PBXBuildRule
PBXContainerItemProxy
PBXCopyFilesBuildPhase
PBXFileReference
PBXFrameworksBuildPhase
PBXGroup
PBXHeadersBuildPhase
PBXLegacyTarget
PBXNativeTarget
PBXProject
PBXReferenceProxy
PBXRezBuildPhase
PBXShellScriptBuildPhase
PBXSourcesBuildPhase
PBXTargetDependency
PBXVariantGroup
XCBuildConfiguration
XCConfigurationList
XCVersionGroup

Just like most of Cocoa, these are fairly self explanatory. But there are a few that stand out.

XCBuildConfiguration

This object holds all of your build settings, including compiler version, provisioning, code signing, info plist, etc. By default, this is not very large because unlike other sections of the project file, it is essentially a diff versus the default build configurations.

PBXNativeTarget

This represents a target for your app. It contains references to the build configuration, build phases, product, etc. This is also how each target can have different settings from the project file. This UID is used both internally to the .pbxproj and externally, as you’ll see later.

PBXFileReference

Yes, this is exactly what you think it is. This is the most common item to find in the project file, and this is usually the spot that gets messed up. This contains the path to the file on disk, as well as meta-info about the file, such as its type. And of course, this also has a UID.

Build scripts/steps etc.

One of the most interesting aspects of the project file are the inclusion of build phases. This is one of the spots where the names don’t quite equate to what you’d assume. And, just like everything else, this section references other objects that represent each kind of build phase. This includes the two most common areas for file conflicts, PBXSourcesBuildPhase and PBXCopyFilesBuildPhase. When working with a version control system like git, these are the areas that most frequently change, and therefore have the most conflicts.

One of the most interesting bits I’ve found is that a run script build phase’s contents are added directly to the project file. So if you’re wondering why Xcode complains about quotes sometimes, this is why. I do not suggest you modify these directly.

Other important files (workspace)

When exploring the contents of the .xcodeproj you’ll notice a few other files floating around. The most important one is the .xcworkspace. As of Xcode 4, every .xcodeproj contains a workspace, but this fact is hidden from the user. This is also how Xcode handles subprojects, and can easily recruse through them. This is just a standard XML file.

The other files of note can be found in the xcschemes directory inside xcshareddata. By default, Xcode assumes that there is a single scheme linked to the single target. If the project contains any shared schemes they will be here. These are also plain XML documents. These contain information on how to build the scheme, with a reference to the UID representing the target in the project file. Just like build phases, if you have any pre- or post-action scripts, they will be included as strings in this file.

In Conclusion

The project file is a formidable document, but it’s not as complicated as most would think. Everything is laid out in an orderly fashion, and once you understand its paradigms you’ll find working with it much easier.

In Part 2 I plan on exploring strategies for some of the common pitfalls we run into when working on a team.

© 2023 Michele Titolo