In the first part of this article (Simple Xcode 9 File Template) we saw how to create a simple Xcode File Template to generate a custom file. Let’s go beyond the Copy/Paste type of template, and see what we can do to make a more dynamic template with options to let the user tailor the generated files.
Table of Contents
In the previous example we saw how to create a basic File Template to generate a single custom file. Now let’s see how we can display a “configuration panel” with text fields, drop down menus and checkboxes, much like the default “Cocoa Touch Class” template that lets you choose a few options before generating one or several files.
For this example, let’s pretend we want a template to create a View, a Model and a ViewModel for an iOS app. We also want to give the opportunity to let the user choose if the view is coming from the Main storyboard, from a XIB file or from code, so the loading code can be generated adequately.
Let’s start with a few simple files for now in this new template:
📂 View, Model & ViewModel.xctemplate 📄 ___FILEBASENAME___.swift 📄 ___FILEBASENAME___View.swift 📄 ___FILEBASENAME___ViewModel.swift 📄 TemplateInfo.plist
So far the View, Model and ViewModel files are pretty barren, we’ll come back to those later:
The PList will be a bit different from the PList of the basic template:
- We won’t need the
AllowedTypeskeys, as we’re not offering the option to chose a single file name anymore.
- We’re introducing an
Optionskey, which is an array of dictionaries. Each dictionary represents an option.
An “option dictionary” has the following keys:
||Label displayed next to the option.||String|
||Description used as a tooltip when the mouse hovers over the option.||String|
||The type of the option, possible values are:
||Whether the wizard should disable the Next button if this option doesn’t have a value.||Bool|
||Uniquely identifies the option and is used to refer to the option’s content as a variable.||String|
||Default value, can use variables from other options (using the strings
||The possible values of a
||Array of String|
||Can be used to enable the current option, only if certain values of another option are selected. For example, only enabling a checkbox for some values of another popup option. The key of the dictionary must be the identifier of the other option, and the value of the dictionary must be the array of subset of values from the
||Can be used to change the order in which the options are displayed, otherwise they are displayed in the order they are entered in the
As long as there’s one option under the
Options key, Xode will show a panel titled “Choose options for your new file”.
Let’s add a name for the View/ViewModel. To do so we need to add the following option to the
Options key, like so:
⚠️ IMPORTANT: Note the
identifier of the option: For Xcode to know that it is the base name you want to use for the files (and to populate the
FILEBASENAMEASIDENTIFIER macro), you need to stick to
The full PList may look like this now:
With this you should see the following screen in Xcode when you select your template:
Note: The description appears if you hover over the label 😉.
Now if you click next, you will see a save dialog, asking you to chose a location, but you won’t be asked for a file name. The name is derived from the
Now let’s amend a bit the source files to make them a bit more useful and let’s start with the
___FILEBASENAME___ViewModel.swift (again, this is for the sake of example, you may implement your ViewModels differently):
When using the template, if the user types “Person” in the name field for instance, the files will be generated with the names:
PersonViewModel.swift, as expected.
However, in a rather confusing but also smart move, Xcode seems to be re-evaluating the
FILEBASENAME macro (and
FILEBASENAMEASIDENTIFIER) in context. Inside the file, the
FILEBASENAME macro has the value “PersonViewModel”, which is the actual final name of the file, and is pretty useful for nameing the class. If you want to have the raw input of what the user typed, the word “Person”, you need to use the value of the
Variables are based on the identifier of the “option” defined in the PList. For an option with identifier
optionName you can access its value with
___VARIABLE_optionName___. You can also add colon separated modifiers to this macro, all of this is explained in the Xcode Text macro format reference.
In our case we want a C identifier from the variable named
productName, so we use
___VARIABLE_productName:identifier___. Yep, it’s a bit wild 😱.
Note: I can’t find a macro modifier to change the case of a variable, and I don’t want to have a class member start with an uppercase character, that’s why in this case I gave it a generic sounding name
model. you could add another option to your template, to ask the user to give you a name for this variable 🤷♀️.
In the same vein, we can amend the view with a function:
Now let’s add two options to ask the user of the template what type of View they want to use, and if they want to have a XIB file generated for them.
We’ll first add a
popup with the following choices:
- XIB file
This is done with the following option:
Now we’ll add a
checkbox to offer to generate a XIB file. But this checkbox should only be active if the XIB file option is selected in the popup menu.
This is done with the following option:
So now your PList should look like this:
And your template options panel should look like this:
For this to actually work we need to go back to the files, and reorganise them. We need a subfolder for each variation in the
.xctemplate folder. The subfolders need to follow a certain naming convention. For a
popup (and I assume it works similarly for a
combo, but I haven’t tested), the subfolders need to match the name of the possible values of the popup, for a
checkbox, it just needs to match the
identifier of the checkbox. If you want to combine values, you just append them in the order they are presented to the user.
This works out to be the following folder structure for our template:
📂 View, Model & ViewModel.xctemplate 📁 Code // Files relevant for a view setup in code 📁 Storyboard // Files relevant for a storyboard view 📁 XIB file // Files relevant for a XIB file based view, without a XIB file 📁 XIB filegenerateXIBfile // Files relevant for a XIB file based view, with a XIB file 📄 TemplateInfo.plist
I know, the folder
XIB filegenerateXIBfile has a pretty ugly name 😨
With all those folders, you can have as many variations of the code, but not all files need to change. In this example, only our
___FILEBASENAME__View.swift needs to change, and a XIB file needs to exist or not. The other two swift files don’t need to change. So to avoid duplicating to much code, I prefer having a “base folder” with the invariant files and hard-linking them into each template folder. Xcode only looks at the folder with names that match the options names, so a
base folder would be invisible and wouldn’t impact the template.
To create a hard link, simply navigate in your terminal to the base folder, and use the following command:
ln model.swift ___FILEBASENAME__.swift
⚠️ IMPORTANT: Once you have created a hard link to a base file, you can’t copy/paste the same hardlinked file in other folders. You need to keep recreating “fresh” hard links.
Repeat the same steps for the ViewModel file, then simply paste a regular copy of the
___FILEBASENAME__View.swift in each template subfolder. You can also create a basic XIB file in Xcode and include it in the ugly named folder
XIB filegenerateXIBfile. In the XIB file you may want to edit the XML to add a
FILEBASENAMEASIDENTIFIER macro to help link it to its base class.
Once you’ve done all of this, you should end up with the following folder structure:
📂 View, Model & ViewModel.xctemplate 📂 base 📄 model.swift 📄 viewModel.swift 📂 Code 📄 ___FILEBASENAME___.swift → model.swift 📄 ___FILEBASENAME___View.swift 📄 ___FILEBASENAME___ViewModel.swift → viewModel.swift 📂 Storyboard 📄 ___FILEBASENAME___.swift → model.swift 📄 ___FILEBASENAME___View.swift 📄 ___FILEBASENAME___ViewModel.swift → viewModel.swift 📂 XIB file 📄 ___FILEBASENAME___.swift → model.swift 📄 ___FILEBASENAME___View.swift 📄 ___FILEBASENAME___ViewModel.swift → viewModel.swift 📂 XIB filegenerateXIBfile 📄 ___FILEBASENAME___.swift → model.swift 📄 ___FILEBASENAME___View.swift 📄 ___FILEBASENAME___View.xib 📄 ___FILEBASENAME___ViewModel.swift → viewModel.swift 📄 TemplateInfo.plist
The customisation if the View files (different
init for different ways of loading the view, depending on the type of view) and the XIB file is left as an exercise for the reader 😁.
You can find the source for this template on GitHub 🐙.
You now have a pretty advanced Xcode template that can generate several files with several variations of your own custom boilerplate. It even lets you choose if you want to generate a XIB file or not. That’s pretty neat!
When I first got interested in Xcode Templates I read a few blog articles, most of them were quite dated (from Xcode 4 or around). The best source for up to date information is to have a look at Xcode’s default templates located in:
Xcode.app/Contents/Developer/Library/Xcode/Templates or Xcode.app/Contents/Developer/Platforms/<platform>/Developer/Library/Xcode/Templates/
An invaluable reference is Steffen Itterheim’s “Xcode 4 Template Documentation”. Steffen made this book free a few years ago, so you have no reason not to grab it. It’s obviously a bit outdated, but is still very helpful.