in Projects, Unity3D

XCode project editor

Today I want to talk about the Xcode Project Editor for Unity that I recently released on GitHub. This year I spent most of my coding time writing custom plugins for Unity to interface our projects with different SDKs for iOS and Android. The biggest issue I bumped into was to automate the build process for custom libraries. The purpose of this project is to programmatically edit an XCode 4 project, directly within Unity, for the ones who need more control of the build process on iOS and Mac.

Reinventing the wheel

The PostProcessBuild script that is automatically executed at build time is a great help, and there are any official tools out there to edit xcode projects by script. After googlin’ around I came across a blog post about a python script by Calvin Rien that fills this gap perfectly. It consists in two main components: a library to read and edit a XCode project file, and a proper PostProcessBuild script to automate the workflow. I’ve used it for all my custom plugins on iOS, until Unity 3.5 released.

Unity 3.5 finally allows to write your postprocess scripts in C# too by the new PostProcessBuild attribute. There is nothing bad about the python editor library, but the new attribute opens a lot more customization opportunities, and any issues on the platform you are running your build process. I found much useful having a C# version of the library with little some changes.

The code is released under MIT license, is open and free to use. Although it’s still under development and needs more testing. and comments are welcome.

The xcodeproject container

Xcode projects are just container folders; it is a common behavior on OSX, just think about the applications and iPhoto. The real project structure data is the project.pbxproj file, inside the main xcodeproj container. There is very little documentation on the pbxproj file format (that is a custom structure of a OpenStep plist file). The PBXParser.cs is in charge to read and write the project file.


Clone the GitHub repo somewhere under Assets/Editor in your project. If your project is not yet checked into git, then you’ll need to do the appropriate setup and add this as a submodule (google: git-submodule). If you already use git for your project, then just add this as a submodule.

For the ones that are not used to git repositories, or that just want to give it a try without setting up everything, there is a unitypackage ready for an easy import.


You can use the XCProject class in any part of your editor and postprocess code. Taking advantage of the great powers of the new PostProcessBuild attribute, I suggest to use a small cs static class to run through all the projmods files in your asses folder and simply apply them to the newly created xcode project.

using UnityEditor;

public static class XCodePostProcess
	public static void OnPostProcessBuild( BuildTarget target, string path )
		// Create a new project object from build target
		XCodeEditor.XCProject project = new XCodeEditor.XCProject( targetPath );

		// Find and run through all projmods files to patch the project
		var files = System.IO.Directory.GetFiles( Application.dataPath, "*.projmods", SearchOption.AllDirectories );
		foreach( var file in files ) {
			project.ApplyMod( file );

		// Finally save the xcode project

The projmods file is a simple text file containing a JSON object. It will be used to pass the parameters to the ApplyMod method. This is the file I use for the GameCenter plugin as a brief example:

	"group": "GameCenter",
	"libs": [],
	"frameworks": ["GameKit.framework"],
	"headerpaths": ["Editor/iOS/GameCenter/**"],					
	"files":   ["Editor/iOS/GameCenter/GameCenterBinding.m",
	"folders": [],	
	"excludes": ["^.*.meta$", "^.*.mdown^", "^.*.pdf$"]
  • group: all files and folders will be parented to this group;
  • libs: add libraries to build phase;
  • frameworks: add frameworks to the project;
  • headerpaths: add header paths to build phase;
  • files: add single files to the project;
  • folders: create a subgroup and add all files to the project (recursive);
  • excludes: file mask to exclude;
Note: all paths are relative to projmods location