Creating Xcode Templates

Creating templates for Xcode projects is not an everyday duty of developers. It’s quite tricky but can be really powerful. It is even possible to customize nib files!

I wanted to create a template for a BSInspectors plugin these days. My intention was to have most of the tedious work done by Xcode when the project is created, so that the developer can focus on writing the code for the plugin.

The first step before the template is created, is to create a sample project. The BSInspector plugin project has two targets, the inspector and the debug object. Both targets have one class each by default. They have a plist with all the settings and the targets build options are also set. That’s nothing special yet.

However, my idea of this plugin was, that the classes for each target are created automatically based on the project name. Creating a project named “Window” should result in a class called WindowInspector and a class called WindowDebugObject. The .m and .h files should be named accordingly of course.

To allow for such fancy stuff, the template has to be setup correctly, which will be described in the rest of this post.

First of all, the sample project has to be moved to Xcodes template folder. That folder is already present for all users at:

   /Library/Application Support/Apple/Developer Tools/Project Templates/

As BSInspector plugins are bundles, the project should go into the “Bundles” subfolder. The most important file in a template is the file “TemplateInfo.plist”. This file needs to be located in the template’s project bundle. To open this bundle, right-click on the xcodeproj file and select show package contents. The project bundle should only contain the “project.pbxproj” file and the “TemplateInfo.plist”. This file is a MacRoman encoded plist! The encoding is the most important part in this whole procedure. I recommend TextMate for editing this plist, but any text-editor should do. The basic layout of this file in text-form is this:

{
  FilesToRename = {
    "SomeExistingHeaderFile.h" = "«PROJECTNAME»HeaderFile.h";
  };
  FilesToMacroExpand = (
    Info.plist,
  );
  Description = "This project builds a plugin for Whatever you want.";
}

«PROJECTNAME» is a so called macro that is replaced by the project name when the new project is created. As the names imply, the first list is a list of files that are renamed when the project is created (semicolon separated!) and the the second list are files that are text-processed during the project creation (comma separated!). In my template i wanted the classes to be named like the project so i ended up with the following file-rename list:

"BSCustomDebugObject.h" = "«PROJECTNAME»DebugObject.h";
"BSCustomDebugObject-Info.plist" = "«PROJECTNAME»DebugObject-Info.plist";
"BSCustomDebugObject.m" = "«PROJECTNAME»DebugObject.m";
"BSCustomInspector.h" = "«PROJECTNAME»Inspector.h";
"BSCustomInspector.m" = "«PROJECTNAME»Inspector.m";
"BSCustomInspector.nib" = "«PROJECTNAME»Inspector.nib";
"BSCustomInspector.xcodeproj" = "«PROJECTNAME»Inspector.xcodeproj";
"BSCustomInspector_Prefix.pch" = "«PROJECTNAME»Inspector_Prefix.pch";

You may notice, that not only the header and implementation files are renamed, but also the project file and the nib file of this template. This leads me to the next part of the plist, the list of files that are macro-expanded. As you may guess, this is the list of most renamed files:

"«PROJECTNAME»DebugObject-Info.plist",
"InspectorInfo.plist",
"«PROJECTNAME».xcodeproj/project.pbxproj",
"English.lproj/InfoPlist.strings",
"«PROJECTNAME»_Prefix.pch",
"«PROJECTNAME»DebugObject.h",
"«PROJECTNAME»DebugObject.m",
"«PROJECTNAME»Inspector.h",
"«PROJECTNAME»Inspector.m",
"«PROJECTNAME»Inspector.nib/classes.nib",
"«PROJECTNAME»Inspector.nib/keyedobjects.nib",

So what is replaced inside these files? The «PROJECTNAME» macro is already known, so why not use it inside the files as well? The header file BSCustomInspector.h looks like this:

//
//  «PROJECTNAME»Inspector.h
//  «PROJECTNAME»
//
//  Created by «FULLUSERNAME» on «DATE».
//  Copyright «YEAR» «ORGANIZATIONNAME». All rights reserved.
//

#import <Cocoa/Cocoa.h>
#import "BSInspector.h"

@interface «PROJECTNAME»Inspector : BSInspector {

}

@end

Notice all those different macros. They’re all taken from the existing templates to provide a consistent file layout throughout the projects.

At any time when creating templates, there’s the opportunity to test the template. Simply create a new project in Xcode with the template that is just being created. Every mistakes made should instantly be visible there.

Creating a new project with the settings from above, you’ll probably see a lot of red files in the project. The problem is that the files are all renamed and processed, but Xcode doesn’t know the new file-names. The solution is to add the macros also to the “project.pbxproj” file in the project bundle. Take care, this file is UTF8 encoded! replace all occurrences of a given filename by it’s macro-filename. Make sure this file is also in the list of macro-expended files. Once this is all done, a new project should not have any red files listed.

BSInspectors also have a GUI. Each inspector has its own window. The nib’s file-owner is set to the inspector-class that it should display. So when we create a plugin with the inspector-class called «PROJECTNAME»Inspector, then we should take care, that the nib file knows about it.

Nib files are bundles like most things in Mac OS X. They typically consist of three files:

classes.nib
info.nib
keyedobjects.nib

The classes and info file are files like the TemplateInfo.plist, but the keyedobjects.nib is a binary plist. Opening the two text-files showed that only the classes.nib contained the class-name I wanted to customize. However, changing it made the nib file impossible to open in InterfaceBuilder. The problem is, that the keyedobjects.nib file also has the classname inside. Customizing the binary plist is not working, so we have to look for another solution.

Fortunately Apple was kind enough to provide us with various tools to modify plists. “plutil” is a command line tool that is made for modifying plists. I’ll use it as a converter in this post, but I’m sure there’re lots of other uses for this little tool. To convert a plist from binary to xml is done with:

plutil -convert xml1 /path/To/plist

This will replace the plist at the supplied path with a XML version of itself. As XML files are text-files they’re process-able like the other files, too. Keep in mind, that XML files are UTF8 encoded!

Changing the classname in the keyedobjects.nib is now no problem anymore and the nib file in new projects is using the inspector-class just created and the the default one.

To sum things up, keep in mind the most dangerous thing when dealing with templates is the file-encoding. The default encoding is MacRoman!! XML property lists and the project.pbxproj file are UTF8 files.

Good luck creating templates!

The template for creating BSInspector plugins is included with the latest BSInspectors archive

Karsten

14 Responses to “Creating Xcode Templates”

  1. rastap Says:

    Thanks Karsten,

    you just filled the single biggest void in the xcode documentation.

  2. Richard Says:

    Thanks for the tremendous instructions. Everything worked great but when quitting my application I get the following message in the Xcode run log “«PROJECTNAME» has exited with status 0.” Any ideas where this “macro” is located?

  3. Karsten Says:

    i would say the file is info.plist and it’s probably not included in the list of files that are macro-expandet. or the encoding is wrong in the file…as i said: encoding is the biggest problem with templates.

  4. Richard Says:

    I found the problem with the extraneous «PROJECTNAME» still remaining during a build. I left the two user files, user.mode1 and user.pbxuser, in the template project bundle by mistake. One or both of them contain the current project name and so need to be removed from the template project bundle as per your instructions. “The project bundle should only contain the “project.pbxproj” file and the “TemplateInfo.plist”.” So there you go, your instructions were 100% accurate. Thanks for the reply and thanks again for posting the instructions.

  5. Karsten Says:

    Yeah, exactly…you must not have more files than these two in your project bunde, maybe i should have made it a bit more clear in the post.

  6. fdiv.net » Xcode Template for Custom Quartz Composer Patches Says:

    […] Frustrated with the tedium of going through all the Xcode project files in a text editor and manually replacing all of the identifiers and filenames when creating a new Quartz Composer Patch, I decided to finally figure out how to create a new Xcode template. It’s pretty straightforward, actually, given Karsten Kusche’s instructions. […]

  7. Jesse Grosjean Says:

    I just created a mini tool that can help with this process http://hogbaysoftware.com/products/xcodetemplatefactory

  8. Gustavo Chaurais Says:

    Don’t you have to use «PROJECTNAMEASIDENTIFIER», instead of «PROJECTNAME» for the file names? Otherwise, if you create a project with a space in the name (ex: “My First Project”, you’ll end up by having file names with space, which is no good. Using «PROJECTNAMEASIDENTIFIER» should fix this problem. It’s also the way Apple implemented iPhone templates.

  9. ThreeE Says:

    Great info. My only problem is that once I modify project.pbxproj to include the macro version of filenames, Xcode is no longer able to parse the project.pbxproj file of the template.

  10. Motti Shneor Says:

    Thanks, this is very good. I spent a lot of time trying to get this missing information about XCode templates, and last I had to decipher it all by myself. It’s assuring to see that my findings match yours. However — There are few notes.

    1. plists ARE xml. I never saw any “binary” version of a plist file. So I don’t quite get what you mean.

    2. If you have XCode installed — You also have the “PList Editor” which gives you easy UI for editing plists. Why bother to work directly on the XML text of it? Search and replace are available, Encoding is handled automatically…. why not use it

    3. Even if you want to edit the plist text by yourself — XCode itself is a good choice, and it does handle file encodings too.

    4. There are two distinct way I saw in Apple’s own templates, to put “Macros” in the template files. One is the «MACRONAME»
    where you must use the templateInfo.plist to explicitly tell XCode to MacroExpand the file, but there is another way, using ___MACRONAME___ instead. (3 underscores before and after the Macro name). These macros are being parsed and expanded even if you do not specify the file’s name in the template’s templateInfo.plist. Apple even uses these macros in the File names in the template directory.

    5. One big mystery is the list of Apple provided Macros (like PROJECTNAME) and their content when compiling a template. What is (to your best knowledge) this list?

    6. There is a new “templateChooser.plist” which allowes creating simple GUI with popup menus and checkboxes, to allow the user to specify the exact kind of template or template-variation he wants. I managed to duplicate Apple’s own templateChooser.plist behaviors, but I don’t know what is POSSIBLE. Have you any ideas?

    Thanks again, and feel free to e-mail me anytime.
    Motti

  11. Karsten Says:

    Hi Motti, thanks for sharing your findings.

    1. Plists can be saved either as XML or as Binary format. Both is supported by Apple’s Plist Editor so you can just try it yourself to save a plist as binary.
    2. i can’t remember why i used the text-editor… probably because of the encoding.
    3. true indeed
    4. nice find!
    5. i have actually no idea
    6. nope, no idea here…

    Karsten

  12. Sunetos, Inc. :: Creating an Xcode project template with GHUnit and OCMock Says:

    […] Creating Xcode Templates (blog post) […]

  13. harsha Says:

    What if I want to create a custom template depending on user input. So that while creating a new project user can actually give some input for choosing various files/components? Is that possible?

  14. Karsten Says:

    sorry, i’ve not yet tried that, but if you find out more, please share!