Skip to content

Creating Xcode 4.x Project Templates

Anton Dryakhlykh edited this page Dec 25, 2017 · 7 revisions
NOTE TO READERS: This information is a work in progress. Most is probably right. Some of it is probably wrong. It is changing frequently. When I feel the documentation is sufficient, I'll remove this warning.

The default Xcode 4.x project templates are located in the Xcode.app bundle. For Mac apps they are here:

Xcode.app/Contents/Developer/Library/Xcode/Templates/Project Templates/ 

and for iOS app they are here:

Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Templates/Project Templates/

For custom templates, you're best to add them to your local library ~/Library/Developer/Xcode/Templates/ so that when you update Xcode it doesn't delete them all on you.

Reference

The only required file in a template is the TemplateInfo.plist file. You can also include additional files alongside your plist that you can then copy into new projects.

TemplateInfo.plist

A typical file looks something like this (which happens to be a template to include a Cocoapods Podfile in the project):

	<?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>Identifier</key>
		<string>com.nsboilerplate.template.project.CocoaPods</string>
	
		<key>Name</key>
		<string>CocoaPods</string>
	
		<key>Description</key>
		<string>NSBoilerplate CocoaPods template.</string>
	
		<key>Kind</key>
		<string>Xcode.Xcode3.ProjectTemplateUnitKind</string>
	
		<key>Ancestors</key>
		<array>
		</array>
	
		<key>Definitions</key>
		<dict>
			<key>../Podfile</key>
			<string>
	#Podfile generated by NSBoilerplate (http://nsboilerplate.com)	
	
	platform :ios, '5.0'

	pod 'BlocksKit', '1.8.1'
	pod 'SSToolkit', '1.0.2'
	pod 'CocoaLumberjack', '1.6'
	pod 'Reachability', '3.1.0'
	pod 'SVProgressHUD', '0.9'
	pod 'AFNetworking', '1.1.0'
	pod 'IAPValidation', '0.0.1'
	pod 'IAPManager', '0.2.0'
	
	</string>

		</dict>
	
		<key>Nodes</key>
		<array>
			<string>../Podfile</string>
		</array>
	
		<key>Options</key>
		<array></array>
	
		<key>SortOrder</key>
		<integer>1</integer>
	
	</dict>
	</plist>

TemplateInfo.plist can contain a number of keys. Here's what each of them do.

Template Identification

Template need te be identified with a unique id and you can optionally include additional descriptors that will appear in Xcode when the templates are selected.

Identifier

This is the unique id of the template. Each template needs to have a different id and this id can be used to reference this template from other templates in ancestry and such.

example:

	<key>Identifier</key>
	<string>com.nsboilerplate.template.project.CocoaPods</string>

Name

The name of the template.

example:

		<key>Name</key>
		<string>CocoaPods</string>

Description

The description of the template.

example:

	<key>Description</key>
	<string>NSBoilerplate CocoaPods template.</string>

Kind

The kind of template. For projects this is always Xcode.Xcode3.ProjectTemplateUnitKind.

example:

	<key>Kind</key>
	<string>Xcode.Xcode3.ProjectTemplateUnitKind</string>

Platforms

Specifies which platforms are allowed to use the templates, for example:

	<key>Platforms</key>
	<array>
		<string>com.apple.platform.iphoneos</string>
	</array>

Concrete

This is a boolean used by Xcode to determine if the templates should be shown in the new projects dialog. If false the template is hidden and if true it's visible. This is useful for creating shared templates of common functionality that are not full project templates in themselves.

example:

	<key>Concrete</key>
	<true/>

Inheritance

Templates can also inherit from one another. This allows multiple templates to be merged into one larger templates, reusing common options and files.

Ancestors

These are a list of the id's of other templates from which this template inherits.

example:

	<key>Ancestors</key>
	<array>
		<string>com.nsboilerplate.template.project.CocoaPods</string>
		<string>com.apple.dt.unit.objectiveCApplication</string>
		<string>com.apple.dt.unit.iPhoneBase</string>
		<string>com.apple.dt.unit.prefixable</string>
	</array>

InjectionTargets

Injections targets are similar to categories in Objective-C. A template that include injection targets will become an ancestor of that target template if both are included in the ancestors of another project template.

example:

	<key>InjectionTargets</key>
	<array>
		<string>com.nsboilerplate.template.project.CocoaPods</string>
	</array>

Content Generation

The purpose of a template is to create and edit a set of files to start a project. These files can be copied from existing files in the template directory or created on the fly based on content of the templates.

Definitions

Here's where things start to get interesting. Definitions list the variables and files in the templates but they're not actually added to the project yet. This is just a list of the things that could be added.

For example, if you want add a given file called README.md to your project, you need to add a definition such as this:

	<key>Definitions</key>
	<dict>
		<key>README.md</key>
		<dict>
			<key>Path</key>
			<string>README.md</string>
			<key>TargetIndices</key>
			<array/>
		</dict>
	</dict>

The keys in the definition start by specifying the file you're editing or creating. In this particular example the file README.md is created by copy it from the template's folder as defined by the dict's Path. I don't want to add the README file to the Copy Bundle Resources build phase so I've also included the TargetIndices with an empty array to specify no targets.

As I mentioned, the definitions dict keys at the actual files so if I had wanted the README placed in a subfolder, that would need to be part of the key. For example, to place it in a Documentation sub directory you would use:

	<key>Docuemtation/README.md</key>

or to place it in the project's parent directory you could use:

	<key>../README.md</key>

Each dict value in the definitions dict has a number of possible keys including:

  • Path — a string to the path of a resource in the template folder that will be copied to the key's location.
  • TargetIndices — anarray of target names to which the file should be added in the Copy Bundle Resources build phase.
  • Group — a string or array representing the Group where the files should be included in the Xcode project file browser. If an array of string elements is defined then each string represents a subgroup. For example "Supporting Files > Documentation" would be:
	<key>Definitions</key>
	<dict>
		<key>README.m</key>
		<dict>
			<key>Group</key>
			<array>
				<string>Supporting Files</string>
				<string>Documentation</string>
			</array>
		</dict>
	</dict>
  • Beginning — a string to add to the beginning of the content.
  • End — a string to add to the end of the content.
  • Indent — an integer that represents the number of indent placeholders the content should be indented.

As well, if the definition dictionary value is a string element, then the content of the element will be used as the content.

	<key>Definitions</key>
	<dict>
		<key>README.md</key>
		<string>This is just an example file.</string>
	</dict>

Nodes

Once you have definitions in place you need to create nodes that create or copy files to your project.

The Nodes array is simply a list of the keys in the Definitions dict that are included in the template. Later, we look at the New Project Wizard, you'll see how to optionally include nodes based on user selections.

Nodes take the format:

	<key>Nodes</key>
	<array>
		<string>DEFINITIONS_KEY</string>
		<string>DEFINITIONS_KEY</string>
		<string>DEFINITIONS_KEY</string>
	</array>

where DEFINITIONS_KEYs are replaced with keys defined in the Definitions element.

Merging Templates and Content

The trick to building a template is using the ancestry, definitions and nodes to assemble a file.

** IS THIS WHAT IT's CALLED???**

In order to merge multiple templates, definition keys can also identify the content area using a sort of namespace appended to the end of the key with a colon (:), for example:

	<key>README.md:installation</key>

and

	<key>README.md:contributors</key>

The content namespaces allow the Xcode template processor to identify multiple segments of a particular file and assemble them together as one. For example, let's look at a few templates that will create a MyClass.m implementation file with a few methods.

First, we'll start in a base template and define an empty method signature and open/close braces for exampleAction:

	<key>Identifier</key>
	<string>com.example.base</string>
	<key>Definitions</key>
	<dict>
		<key>MyClass.m:exampleAction</key>
		<dict>
			<key>Beginning</key>
			<string>- (void)exampleAction
{
	// Just an example.
			</string>
			<key>End</key>
			<string>
}
			</string>
			<key>Indent</key>
			<integer>1</integer>
		</dict>
	</dict>
	<key>Nodes</key>
	<array>
		<string>MyClass.m:exampleAction</string>
	</array>

This com.example.base template includes a definition for MyClass.m:exampleAction. If we created a new project based on this template we would end up with a MyClass.m file with the following exampleAction method:

	- (void)exampleAction
	{
		// Just an example.
	}

So far so good. But let's say that in another template, we want to add some code inside exampleAction's implementation. For that, we would set the template's ancestor to be com.example.base and define an additional suffix on MyClass.m:exampleAction

	<key>Identifier</key>
	<string>com.example.setup</string>
	<key>Ancestors</key>
	<array>
		<string>com.example.base</string>
	</array>
	<key>Definitions</key>
	<dict>
		<key>MyClass.m:exampleAction:setup</key>
		<string>self.label.text = @&quot;Hello&quot;</string>
	</dict>
	<key>Nodes</key>
	<array>
		<string>MyClass.m:exampleAction:setup</string>
	</array>

Now, if we were to use the com.example.setup template to create a project, MyClass.m would contain:

	- (void)exampleAction
	{
		// Just an example.
		self.label.text = @"Hello";
	}

You'll notice that XML special characters such as &quot; need to be escaped in the template and the resulting code was indented properly due to the <indent> set in the definition of MyClass.m:exampleAction in com.example.base.

Now, let's extend this further and add another template that will insert code before the self.label line as well as add another method.

	<key>Identifier</key>
	<string>com.example.colours</string>
	<key>Ancestors</key>
	<array>
		<string>com.example.setup</string>
	</array>
	<key>Definitions</key>
	<dict>
		<key>MyClass.m:exampleAction:setup</key>
		<dict>
			<key>Before</key>
			<string>self.label.color = [self color];</string>
		</dict>
		<key>MyClass.m:color</key>
		<string>
	- (UIColor *)color {
		return [UIColor redColor];
	}
		</string>
	</dict>
	<key>Nodes</key>
	<array>
		<string>MyClass.m:exampleAction:setup</string>
		<string>MyClass.m:color</string>
	</array>

Now, the resulting MyClass.m would contain:

	- (void)exampleAction
	{
		// Just an example.
		self.label.color = [self color];
		self.label.text = @"Hello";
	}

	- (UIColor *)color {
		return [UIColor redColor];
	}

Definition keys can also include wildcards, for example:

	<key>*:implementation:dealloc</key>

or

	<key>*:implementation:dealloc:*</key>

These definitions are defined using some generic strings and variables.

	<key>Definitions</key>
	<dict>
		<key>*:implementation:dealloc</key>
		<dict>
			<key>Beginning</key>
			<string>- (void)dealloc
	{</string>
			<key>End</key>
			<string>	[super dealloc];
	}
	</string>
			<key>Indent</key>
			<integer>1</integer>
		</dict>
		<key>*:implementation:dealloc:*</key>
		<string>[___*___ release];</string>
	</dict>

By using the special ___*___ wildcard variable in the definition, the Nodes array can then be used to directly insert content based on the path name, for example:

	<key>Nodes</key>
	<array>
		<string>MasterViewController.m:implementation:dealloc:_detailViewController</string>
	</array>

creates:

	- (void)dealloc
	{
		[_detailViewController release];
		[super dealloc];
	}

The wildcard represents the remaining value in the key.

Keys and other parts of the template can use other constants as well.

Constants

Constants in TemplateInfo.plist files are identified by a triple underscore (___) on either side of the constant's name, such as:

___PACKAGENAME___Class.m

where ___PACKAGENAME___ contains the actual package name so the resulting implementation file would have the appropriate name.

There are several different pre-defined constants you can use including:

  • ___COPYRIGHT___ – the copyright message.
  • ___DATE___ — the current date when the new project is created.
  • ___DIRECTORY___ — the full path of directory in which the new file is being created.
  • ___FILEBASENAME___ — the current file name without extension.
  • ___FILEBASENAMEASIDENTIFIER___ — same as FILEBASENAME, but modified to be a legal C-style identifier.
  • ___FILEEXTENSION___ — the extension of the file.
  • ___FILENAME___ — the file name (with extension) as typed by the user.
  • ___FULLUSERNAME___ — the full name of the current Xcode user.
  • ___ORGANIZATIONNAME___ — the organization name as specified in the New Project wizard.
  • ___PACKAGENAME___ — package name entered by the user in the New Project wizard.
  • ___PARENTPACKAGENAME___
  • ___PROJECTNAME___ — the name of the project in which file is created, empty if no project.
  • ___PROJECTNAMEASIDENTIFIER___ — same as PROJECTNAME, but modified to be a legal C-style identifier.
  • ___TIME___ – the current time when the file is created.
  • ___USERNAME___ – the account name of the logged-in user.

Along with there there are others I've seen prefixd with IMPORTHEADER but I haven't figured out where they come from yet.

  • ___IMPORTHEADER_categoryClass___
  • ___IMPORTHEADER_extensionClass___
  • ___IMPORTHEADER_cocoaTouchSubclass___

Constants can also be created via Macros and from the New Project Wizard Options.

Macros

Macros are a way of defining your own constants in templates. For example, here's a macro that's used in the ARC Template

	<key>Macros</key>
	<dict>
		<key>AR</key>
		<string> autorelease]</string>
		<key>RP</key>
		<string>[</string>
		<key>RT</key>
		<string> retain]</string>
	</dict> 

These macros define three constants:

  1. AR which outputs autorelease]
  2. RP which outputs [
  3. RT which outputs retain]

Using these constants, you could write template output using the following format:

self.window = ___RP___[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]___AR___;

combined with the constants above, the output would be

	self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];

However, in the case of the ARC template, if the user opts to use ARC instead, then the macros are defined with empty strings:

	<key>Macros</key>
	<dict>
		<key>AR</key>
		<string></string>
		<key>RP</key>
		<string></string>
		<key>RT</key>
		<string></string>
	</dict>

and the same template content would result in ARC complient code without the autorelease and opening bracket:

	self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

See below for more on the New Project Wizard.

The last group of variables are also directly related to the New Project Wizard and they're the variables that represent the chosen options. For example, if the template asks the user for a class name, the option might use userSubclass as the option name, in which case the template would also include a constant with that variable's value. These constants follow the same format but are also prefixed with VARAIBLE like this:

___VARIABLE_userSubclass___

Common examples in Apple's templates include:

  • ___VARIABLE_classPrefix:identifier___
  • ___VARIABLE_categoryClass:identifier___
  • ___VARIABLE_extensionClass:identifier___
  • ___VARIABLE_extensionName:identifier___
  • ___VARIABLE_cocoaTouchSubclass___

New Project Wizard

By combining Definitions, Nodes and Variables you can create powerful templates but to go further you may need to ask the project creator some questions about the project before it's created. For example, Apple's default templates ask for things like the class prefix, company name, projects name and what features you'd like to include in this particular project type.

The Options element is an array of dicts that define each option in the New Project Wizard. Each option can include:

Identifier

An identifier which is like a variable name that represents the value of the option, for example:

	<key>Identifier</key>
	<string>useARC</string>

As described earlier, a constant with the selected value will be created in the format ___VARIABLE_identifier___, for example ___VARIABLE_userARC___.

Description

A general description of what the option is for, which may be used for additional contextual help, for example:

	<key>Description</key>
	<string>Whether the new project should use Automatic Reference Counting</string>

Default

The default value for the option, for example:

	<key>Default</key>
	<string>true</string>

SortOrder

An integer used to sort the options in the New Project Wizard, for example:

	<key>SortOrder</key>
	<integer>2</integer>

Name

A label

	<key>Name</key>
	<string>Category</string>

NotPersisted

	<key>NotPersisted</key>
	<true/>

Required

	<key>Required</key>
	<true/>

RequiredOptions

	<key>RequiredOptions</key>
	<dict>
		<key>cocoaTouchSubclass</key>
		<string>UITableViewController</string>
	</dict>

'Type'

The Type defines what sort of input you're expecting. Type can be one of:

static

A static option will show the values as static text in the wizard. If the option already has a value (identified by the Identifier) then it will display the current value, otherwise it will display the Default. This is useful for displaying the outcome of a result after selecting different options.

	<dict>
		<key>Identifier</key>
		<string>aStaticString</string>
		<key>Name</key>
		<string>A static example</string>
		<key>Default</key>
		<string>Hello World</string>
		<key>Type</key>
		<string>static</string>
	</dict>

Image of static in New Project Wizard

checkbox (and Units)

Checkboxes allow you to create boolean options by displaying checkbox. For a checkbox to work properly you also need to include the Units key with a set of Definitions and Nodes that will be applied depending on the boolean selection. For example, here we offer a checkbox to include a README file:

	<dict>
		<key>Default</key>
		<string>true</string>
		<key>Description</key>
		<string>Whether the new project should include a README file</string>
		<key>Identifier</key>
		<string>useREADME</string>
		<key>Name</key>
		<string>Include a README</string>
		<key>SortOrder</key>
		<integer>1</integer>
		<key>Type</key>
		<string>checkbox</string>
		<key>Units</key>
		<dict>
			<key>false</key>
			<array>
			</array>
			<key>true</key>
			<array>
				<dict>
					<key>Definitions</key>
					<dict>
						<key>README</key>
						<string>Enter your readme info here....</string>
					</dict>
					<key>Nodes</key>
					<array>
						<string>README</string>
					</array>
				</dict>
			</array>
		</dict>
	</dict>

Image of checkbox in New Project Wizard

text

Text allows you to add simple text inputs for things like project / class names or API keys.

	<dict>
		<key>Description</key>
		<string>Your API key</string>
		<key>Identifier</key>
		<string>apiKey</string>
		<key>Name</key>
		<string>Service API Key</string>
		<key>Required</key>
		<true/>
		<key>Type</key>
		<string>text</string>
	</dict> 

Image of text in New Project Wizard

class

Class is a pre-defined list of the project's available classes.

	<dict>
		<key>Default</key>
		<string>NSObject</string>
		<key>Description</key>
		<string>Which class to add a category to</string>
		<key>Identifier</key>
		<string>categoryClass</string>
		<key>Name</key>
		<string>Category on</string>
		<key>Required</key>
		<true/>
		<key>Type</key>
		<string>class</string>
	</dict>

popup

If you want to show a single choice of multiple items you can use popup with the values specified in Values.

	<dict>
		<key>Identifier</key>
		<string>deviceFamily</string>
		<key>Name</key>
		<string>Device Family</string>
		<key>Description</key>
		<string>What device family.</string>
		<key>Type</key>
		<string>popup</string>
		<key>Default</key>
		<string>iPhone</string>
		<key>Values</key>
		<array>
			<string>iPhone</string>
			<string>iPad</string>
		</array>
	</dict>

Image of popup in New Project Wizard

buildSetting

Including a buildSetting options allows you to update the buld settings specified in Name with the values specified in Values. For example:

	<dict>
		<key>Identifier</key>
		<string>usingARC</string>
		<key>Name</key>
		<string>CLANG_ENABLE_OBJC_ARC</string>
		<key>RequiredOptions</key>
		<dict>
			<key>cocoaTouchSubclass</key>
			<string>UITableViewController</string>
		</dict>
		<key>Type</key>
		<string>buildSetting</string>
		<key>Values</key>
		<array>
			<string>NO</string>
		</array>
	</dict>

Options across templates

You can use the values of options from one template in another. Using our README example from earlier, another template could include

	<key>Options</key>
	<array>
		<dict>
			<key>Identifier</key>
			<string>useREADME</string>
			<key>Units</key>
			<dict>
				<key>true</key>
				<!-- other things to do if useREADME is true -->
			</dict>
		</dict>
	</array> 

Build Properties

Project

The Project dictionary specifies project level settings in either the Configurations key (allowing you to specify settings for individual configurations) or the SharedSettings key (applies to all configurations). The configureable keys are any of the standard build settings you're familiar with.

	<key>Project</key>
	<dict>
		<key>Configurations</key>
		<dict>
			<key>Release</key>
			<dict>
				<key>VALIDATE_PRODUCT</key>
				<string>YES</string>
				<key>OTHER_CFLAGS</key>
				<string>-DNS_BLOCK_ASSERTIONS=1</string>
			</dict>
		</dict>
		<key>SharedSettings</key>
		<dict>
			<key>TARGETED_DEVICE_FAMILY</key>
			<string>1,2</string>
			<key>CODE_SIGN_IDENTITY[sdk=iphoneos*]</key>
			<string>iPhone Developer</string>
			<key>GCC_WARN_ABOUT_RETURN_TYPE</key>
			<string>YES</string>
			<key>GCC_WARN_UNUSED_VARIABLE</key>
			<string>YES</string>
		</dict>
	</dict>

Targets

Targets are very similar to the Project group, however they apply to the target level build's settings. Along with Configurations and SharedSettings you can specify an array of Frameworks to include in the target and a Name for the target.

	<key>Targets</key>
	<array>
		<dict>
			<key>Configurations</key>
			<dict>
				<key>Debug</key>
				<dict/>
				<key>Release</key>
				<dict/>
			</dict>
			<key>Name</key>
			<string>___PACKAGENAME___</string>
			<key>SharedSettings</key>
			<dict>
				<key>ALWAYS_SEARCH_USER_PATHS</key>
				<string>NO</string>
				<key>PRODUCT_NAME</key>
				<string>$(TARGET_NAME)</string>
			</dict>
			<key>Frameworks</key>
			<array>
				<string>UIKit</string>
				<string>Foundation</string>
				<string>CoreGraphics</string>
			</array>
		</dict>
	</array>

end.