About Xcode Configuration Files

About Xcode Configuration Files

2018, Apr 24    

Usually some of the developers just ignore the .xcworkspace and .xcodeproj files which auto generated in the working directory. But indeed, they played important roles in organising the project files structure and helping to separate sharable configuration and those configuration which belongs to individual developers.

A Xcode workspace always exists, and may be external to an .xcodeproj, or be embedded within the .xcodeproj file dir. When the xcode project only contains a .xcodeproj dir, it belongs to the second case.

# inside of an xxx.xcodeproj folder
├── project.pbxproj
├── project.xcworkspace
│   ├── contents.xcworkspacedata
│   ├── xcshareddata
│   │   └── IDEWorkspaceChecks.plist
│   └── xcuserdata
│       └── ${username}.xcuserdatad
│           └── UserInterfaceState.xcuserstate
└── xcuserdata
    └── ${username}.xcuserdatad
        └── xcschemes
            └── xcschememanagement.plist

About .xcworkspace and .xcodeproj files

Case: .xcodeproj only

  • project.pbxproj : is a xcode-specific file using .plist format, which came from NeXt and is now only used by legacy programs(and Xcode). Also interestingly, Xcode by default is reading and writing the ASCII plist format, but can also read the XML plist format. It contains the majority of information related to xcode build-settings and build phases configurations.
  • project.xcworkspace : contains those content which can be further shared among developers. For example, IDEWorkspaceChecks provides optimisation to shrink time which takes to launch the xcode project.
  • UserInterfaceState.xcuserstate : contains the interface status for each of the user, such as cursor and window position.
  • contents.xcworkspacedata : in a standalone xcode project, the embedded worspacedata provides the description aboout the location of the xcworkspace file. The only possible use cases I can guess is that in Xcode you can transform a xcproject into a xcworkspace, and using this file the IDE can locate the location of the .xcodeproj:
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
   version = "1.0">
   <FileRef
      location = "self:Test.xcodeproj">
   </FileRef>
</Workspace>
  • xcschememanagement.plist : under the xcuserdata dir, it contains the organised list of building targets for each of the user. An example of xcschememanagement file is given below:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
 "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>SchemeUserState</key>
	<dict>
		<key>%{target1}%.xcscheme</key>
		<dict>
			<key>orderHint</key>
			<integer>1</integer>
		</dict>
		<key>%{target2}%.xcscheme</key>
		<dict>
			<key>orderHint</key>
			<integer>0</integer>
		</dict>
	</dict>
</dict>
</plist>

Case: .xcodeproj with .xcworkspace

It has been quite a while that Xcode supports organising targets under the same workspace. Let’s take a look at the configuration file structure under and normal workspace for multiple projects. Assume that both of the projects reside under the same directory of .xcworkspace file. Now you can see that the original .xcworkspacedata, xcshareddata, xcuserdata under the .xcodeproj dir now has been moved to the .xcworkspace directory, serving as overal meta info to locate all the xcode project which belongs to the workspace.

# check about local
├── ${workspace_name}.xcworkspace
│   ├── contents.xcworkspacedata
│   ├── xcshareddata
│   │   ├── ${workspace_name}.xcscmblueprint
│   │   ├── IDEWorkspaceChecks.plist
│   │   └── WorkspaceSettings.xcsettings
│   └── xcuserdata
│       ├── ${username}.xcuserdatad
│       │   └── UserInterfaceState.xcuserstate
│       └── contents.xcworkspacedata
├── ${project1}.xcodeproj
│   ├── project.pbxproj
│   └── xcuserdata
│       └── ${project1}.xcuserdatad
│           └── xcschemes
│               ├── ${project1}.xcscheme
│               └── xcschememanagement.plist
└── ${project1}.xcodeproj
    ├── project.pbxproj
    └── xcuserdata
        └── ${username}.xcuserdatad
            └── xcschemes
                └── xcschememanagement.plist
  • xccheckout and xcscmblueprint stores source code management information.
  • contents.xcworkspacedata points to where the .xcodeproj files located, which belongs to this .xcworkspace.

Deep dive for project.pbxproj

As in the previous section, we mentioned, project.pbxproj is the file containing most of the xcode configuration information. In this section, we provide an overall sketch for the files internal structure.

File Content

Create a hello world project and convert the project into a workspace. Then open the project.pbxproj located under the .xcodeproj dir. The file’s internal structure can be listed as below:


/* Begin PBXBuildFile section, declares all types of resources, classes, binaries, dependencies used in build process. */
	25CE60C42419EBA2008B469A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 25CE60C22419EBA2008B469A /* Main.storyboard */; };
	25CE60CC2419EBA5008B469A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 25CE60CB2419EBA5008B469A /* main.m */; };
	A3764AE544D4236FF399D13C /* libPods-Test.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 40564B11A9B88ADF3FEAAC13 /* libPods-Test.a */; };

/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
/* End PBXContainerItemProxy section */

/* Begin PBXCopyFilesBuildPhase section, declares all the copy resource actions. */
	25EB5F162428D38600998E74 /* Embed Frameworks */ = {
		isa = PBXCopyFilesBuildPhase;
		buildActionMask = 2147483647;
		dstPath = "";
		dstSubfolderSpec = 10;
		files = ();
		name = "Embed Frameworks";
		runOnlyForDeploymentPostprocessing = 0;
	};
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section, declares all files in organized project IDE file browser, no matter whether it is related to the build process or not.*/
	25CE60B62419EBA2008B469A /* Test.app */ = {isa = PBXFileReference; explicitFileType = wrapper.pplication; includeInIndex = 0; path = Test.app; sourceTree = BUILT_PRODUCTS_DIR; };
	25CE60B92419EBA2008B469A /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
	25CE60BA2419EBA2008B469A /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
	25CE60BC2419EBA2008B469A /* SceneDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SceneDelegate.h; sourceTree = "<group>"; };
	25CE60BD2419EBA2008B469A /* SceneDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SceneDelegate.m; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section, phases for build and link framework binaries */
	25CE60B32419EBA2008B469A /* Frameworks */ = {
		isa = PBXFrameworksBuildPhase;
		buildActionMask = 2147483647;
		files = (
				A3764AE544D4236FF399D13C /* libPods-Test.a in Frameworks */,
		);
		runOnlyForDeploymentPostprocessing = 0;
	};
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section, an array of folder modles, but can based on Id to construct a file tree. */
	// 25CE60AD2419EBA2008B469A = {
		isa = PBXGroup;
		children = (
			25DB6E8E2420A2A900056788 /* WalletAPI.swift */,
			25CE60B82419EBA2008B469A /* Test */,
			25CE60D42419EBA5008B469A /* TestTests */,
			25CE60DF2419EBA5008B469A /* TestUITests */,
			25CE60B72419EBA2008B469A /* Products */,
			25CE60EE2419EBE1008B469A /* Frameworks */,
			25CE60F22419F059008B469A /* Test-Bridging-Header.h */,
			F050E18E923DCED15DFFFD20 /* Pods */,
		);
		sourceTree = "<group>";
	};
/* End PBXGroup section */

/* Begin PBXHeadersBuildPhase section, phases for specifiying ObjC headers in build process.*/
/* End PBXHeadersBuildPhase section */

/* Begin PBXShellScriptBuildPhase section */
/* End PBXShellScriptBuildPhase section */

/* Begin PBXResourcesBuildPhase section */
/* End PBXResourcesBuildPhase section */

/* Begin PBXSourcesBuildPhase section */
/* End PBXResourcesBuildPhase section */

/* Begin PBXNativeTarget section, specifies project build target, and all the build phases which belongs to each of the build target */
	25CE60B52419EBA2008B469A /* Test */ = {
		isa = PBXNativeTarget;
		buildConfigurationList = 25CE60E52419EBA5008B469A /* Build configuration list for PBXNativeTarget "Test" */;
		buildPhases = (
			CC6D41A53030B4F832F09879 /* [CP] Check Pods Manifest.lock */,	
			25CE60F52419F173008B469A /* Headers */,
			25CE60B22419EBA2008B469A /* Sources */,
			25CE60B32419EBA2008B469A /* Frameworks */,
			25CE60B42419EBA2008B469A /* Resources */,
			25EB5F162428D38600998E74 /* Embed Frameworks */,
			240DA6142DC0CAC7A888CA9F /* [CP] Copy Pods Resources */,
		);
		buildRules = ();
		dependencies = ();
		name = Test;
		productName = Test;
		productReference = 25CE60B62419EBA2008B469A /* Test.app */;
		productType = "com.apple.product-type.application";
	};
/* End PBXNativeTarget section */

/* Begin PBXProject section, specifies build target corelations in this project, as well as project root path, build tool versions, etc. */
/* End PBXProject section */

/* Begin PBXTargetDependency section, specifies any of the target is served as a dependency unit for other target, which means during the build phase, build of target A will also trigger the build of all the dependencies of target A*/
	25CE60D32419EBA5008B469A /* PBXTargetDependency */ = {
		isa = PBXTargetDependency;
		target = 25CE60B52419EBA2008B469A /* Test */;
		targetProxy = 25CE60D22419EBA5008B469A /* PBXContainerItemProxy */;
	};
/* End PBXTargetDependency section */

/* Begin PBXVariantGroup section, xib files and storyboard */
	25CE60C22419EBA2008B469A /* Main.storyboard */ = {
		isa = PBXVariantGroup;
		children = (
			25CE60C32419EBA2008B469A /* Base */,
		);
		name = Main.storyboard;
		sourceTree = "<group>";
	};
/* End PBXVariantGroup section */

/* Begin XCBuildConfiguration section, build configuration section for the project and the targets under the project. - Default is Debug/Release, user could customise more */
/* End XCBuildConfiguration section */

/* Begin XCConfigurationList section, goup a list of configuration and bind them to project and the targets under the project */
/* End XCConfigurationList section */

Component Correlation

We can further summarise the components into a class diagram to depict the model correlations. For every PBXProject contains multiple list of PBXNativeTargets. The project and targets can own their own lists of build configurations. For each of the the PBXNativeTargets, a list of build phases are included which further helps to organize the list of build tasks in the IDE’s build phase tab.

Screenshot 2020-05-20 at 9 03 46 PM

There are libraries for parsing project.pbxproj file such as xcodeproj. Could be discussed in other blogs.

TOC