When doing iOS development in Xcode, it's very common to use one of Xcode's many file templates, to create files that are pre-populated with code that you need for a specific purpose. What is not as common however is creating your own custom templates, to speed up development time and reduce boilerplate code writing. This can be very useful, especially if you are using an architecture such as MVVM, VIP or any other that has a collection of semantically named files associated with each new component. Luckily for us, with Apple's Xcode 9, this templating is relatively pain free to achieve.
For this demonstration, I'm going to be creating a template to use when writing code following the VIP architecture. For those of you who are unfamiliar with this paradigm, there are three core files we need when creating a new component (i.e. screen) for a VIP app:
- View (For iOS, this is often a ViewController)
We may also want a storyboard or xib, that we can then use to connect to our
View file. For the rest of this tutorial, this is going to be the target in mind; to create a template that generates these files all in one go, possibly with some nice additions along the way.
To get started, we need to look at where our custom templates will live in the file system, as well as where the system templates given to us by Xcode reside.
All built in templates are located in this directory:
The custom templates that we write will need to go into this directory:
Instead of writing a template from scratch, we're just going to copy one of Xcode's built in templates, then edit it to our liking. To do this, we first need to create a folder to hold our custom category of templates. Then, we need to copy one of the existing Xcode templates into this folder.
mkdir -p ~/Library/Developer/Xcode/Templates/VIP cp -R /Applications/Xcode.app/Contents/Developer/Library/Xcode/Templates/File\ Templates/Source/Swift\ File.xctemplate ~/Library/Developer/Xcode/Templates/VIP/
As can be seen above, a folder is created called VIP, then the file Swift File.xctemplate is copied into this folder as a starting point. The folder name can be anything you like, as it will be used as the category name you see to group different template types together. You can even create multiple folders, each grouping together different custom templates.
To see if what we have done has worked, jump over to Xcode 9 and open the new file menu. If you scroll to the very bottom of the template selection menu, you should see our first custom template ready to be selected.
What's In A Template
Before we dive any deeper, we need to examine exactly what a template is composed of internally. If we navigate to the just created template folder, we can see that it is made up of four files:
___FILEBASENAME___.swift file is the actual swift file that will be duplicated when creating our template. In here you can put any code and/or comments you want to be present every time you create a new instance of your template. An important thing to note about this is that we can actually add as many of these files as we want. In fact any file we place in this folder (with the exception of the
TemplateIcon.png images and
TemplateInfo.plist) will be copied into our project every time we initialise the template. We'll return to this later.
TemplateIcon@2x.png are used to show an icon that represents your template. As we copied ours from the generic "Swift File.xctemplate", this is just the swift logo on a document. However when creating our own templates, we can switch this out to be whatever we like.
TemplateInfo.plist file contains all of the configuration information about your template. This includes things such as its name, description, kind as well as possible options for when you are initialising your template.
Adding Custom Icons
One of the easiest things to do when creating a template is to give it a custom icon, which can be achieved by simply changing the images in the template folder. The sizes of the two icon images are as follows:
Use whatever image creator you're comfortable with and replace these files with your newly created images. Be sure to keep the names the same, as this is what is used to distinguish between your icon image, and any images that may be included in your template. Jump over to Xcode and you should now see your new icon displayed in the new file selection window.
Generating Multiple Files
The next thing to look at is generating the files we need for our template. As stated earlier, there are three core files needed for the VIP component, these being a View, an Interactor and a Presenter. Luckily for us, we can generate multiple swift files int he one template.
Jump over to the template folder and make the following copies
cp ___FILEBASENAME___.swift ___FILEBASENAME___ViewController.swift cp ___FILEBASENAME___.swift ___FILEBASENAME___Interactor.swift cp ___FILEBASENAME___.swift ___FILEBASENAME___Presenter.swift
After this has been done, we can then remove the default source file, as it is no longer required.
Now that our three core file templates exist, we are able to add any custom code and/or documentation we would want to be included in these files when they are generated. As this step is fairly straight forward, I'm just going to leave these files as they are.
If you want to see what the template looks like in action now, jump back to Xcode, select our template from the new file menu and provide a name, such as "Camera". This should then generate the following files.
While what we have achieved so far would completely suffice as a useful template, we're going to take it a step further. What if we want to optionally be able to also generate a Storyboard file, and have it also incorporate the semantic naming. Maybe we want the user to be able to choose if they want a Storyboard, XIB or nothing. Well for this kind of behaviour, we are going to need to look more closely at the
Open the file up and add the following into the base
<dict> in the xml.
(You may want to open up the file as "source code" rather than the default Xcode table format, to make the copy easier)
<key>Options</key> <array> <dict> <key>Identifier</key> <string>productName</string> <key>Required</key> <true/> <key>Name</key> <string>Name:</string> <key>Description</key> <string>The name of the VIP component to create</string> <key>Type</key> <string>text</string> <key>Default</key> <string></string> </dict> <dict> <key>Identifier</key> <string>viewType</string> <key>Required</key> <true/> <key>Name</key> <string>View type:</string> <key>Description</key> <string>The type of view to use</string> <key>Type</key> <string>popup</string> <key>Default</key> <string>Storyboard</string> <key>Values</key> <array> <string>Storyboard</string> <string>XIB</string> <string>None</string> </array> </dict> </array>
There are two "options" we're adding here, each achieving a different goal.
The first dictionary in the options array is adding a specific entry field for the user to write the component name. Previously this was done inline when choosing the save destination, but seeing as we are actually generating multiple files, this layout makes more sense.
The second option is adding a drop down menu, that allows the developer to select from three different options:
These options will map to folders in our template folder, meaning that when the user selects the "Storyboard" option, Xcode will look for a folder named "Storyboard" in the template, and use any swift files that are located in there. Therefore to get this new behaviour working correctly, we now need to add these folders.
Bringing It All Together
To make this all work, we are going to need to go back to our template folder and add three new subfolders to match our three options, and copy the source files into these folders.
mkdir -p Storyboard XIB None cp ___FILEBASENAME___Interactor.swift ___FILEBASENAME___Presenter.swift ___FILEBASENAME___ViewController.swift Storyboard cp ___FILEBASENAME___Interactor.swift ___FILEBASENAME___Presenter.swift ___FILEBASENAME___ViewController.swift XIB cp ___FILEBASENAME___Interactor.swift ___FILEBASENAME___Presenter.swift ___FILEBASENAME___ViewController.swift None
Now that the source files have been copied, we can delete the three in the root of the template.
rm ___FILEBASENAME___Interactor.swift ___FILEBASENAME___Presenter.swift ___FILEBASENAME___ViewController.swift
(Note: Instead of creating duplicated of the source code files, you may wish to instead use hard links)
The final step now is to simply add a
___FILEBASENAME___.storyboard to the Storyboard folder, and a
___FILEBASENAME___.xib to the XIB folder. The simplest way to do this is to simply copy them over from their corresponding Xcode template like so.
cp -R /Applications/Xcode.app/Contents/Developer/Library/Xcode/Templates/File\ Templates/User\ Interface/Storyboard.xctemplate/___FILEBASENAME___.storyboard ~/Library/Developer/Xcode/Templates/VIP/VIP\ Component.xctemplate/Storyboard` cp -R /Applications/Xcode.app/Contents/Developer/Library/Xcode/Templates/File\ Templates/User\ Interface/View.xctemplate/___FILEBASENAME___.xib ~/Library/Developer/Xcode/Templates/VIP/VIP\ Component.xctemplate/XIB`
With this done, our template should now be ready to use. Open Xcode and go to the new file menu, select the template and you should see a popup allowing you to enter a name, as well as select the view type.
Where to from here
There is plenty more to explore here in the realm of Xcode templates, as well as more to look at in terms of Xcode customisations in general. I strongly encourage any of you interested to look through the various built in templates to see all of the different options available to you.