- briksoftware Blog - https://briksoftware.com/blog -

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 [1]

Karsten