原文地址:https://www.raywenderlich.com/26582967-xcode-project-and-file-templates
If you’re tired of filling out boilerplate code whenever you begin a new Xcode project, you can save yourself a ton of time by creating customized project templates.
Using Xcode’s project and file templates
, you have the power to customize how new projects and files start out their life from the moment you use File ▸ New
. These templates allow you to pre-fill the contents of new files and project so that you can get to work on the core of your next excellent app idea more quickly.
In this tutorial, you’ll learn:
- What Xcode templates are and where to find the default templates that ship with Xcode.
- The anatomy of Xcode’s templates.
- How to create project templates.
- How to create file templates.
- Getting input from users when they use a template.
Get ready because you’re about to replace some unnecessary boilerplate with custom Xcode templates!
Getting Started
First, click the Download Materials
button at the top or bottom of this tutorial to download the project materials.
In this tutorial, you’ll build an app called Stellar Space
which uses NASA’s Astronomy Picture of the Day API to show today’s latest space-related and wallpaper-worthy photo. Along the way you’ll how to create your own templates.
Note: The starter materials don’t contain a starter Xcode project. That’s because you’ll be creating the project by yourself, using custom Xcode project templates!
Imagine for a moment that you are a big fan of the Model-View-ViewModel (MVVM) design pattern. You want to use it in all your apps, and this app is no different. Usually, a new SwiftUI app comes with a ContentView.swift for you to use for the first screen of your app. But instead of using the default template, you’ll create your own template that pairs ContentView
with a view model to separate the UI and business logic. Using that template will make your life easier when creating your projects.
Since Xcode’s templating system is robust but poorly documented, you’ll need to explore the default templates Xcode offers so you can know what you can customize.
Understanding Default Templates
Every time you’ve created a new Xcode project in the past, you did so using Xcode’s default templates. These default templates make things easier than starting with a blank project.
Templates decide what your new project will contain. The default templates include many different options including, but not limited to:
- A blank view with a “Hello, World!” text.
- Setup code for your Core Data implementation
- A fully functioning ARKit scene
Xcode’s rich and complex templating system also allows user input, conditional logic and many combinations of files. Each template also offers inheritance, meaning the templates you create can adopt logic from one another or the default templates.
Inheritance also means that all the fantastic behavior of the default templates is up for grabs. Consequently, looking at the default templates is a great way to start creating templates of your own.
Exploring Base Templates
Xcode stores the default templates within the Xcode app bundle itself. To take a look at all the base templates, open a Finder window. You can do this by opening Finder and pressing Shift-Command-G or going to Go ▸ Go to Folder…. Then, copy and paste the location of the default project templates into the resulting window:
/Applications/Xcode.app/Contents/Developer/Library/Xcode/Templates/Project Templates
Next, click Go.
The Base folder contains templates that you won’t see directly in Xcode’s New Project dialog. These base templates are abstract, and they serve as good starting points that other templates can inherit from.
The Base folder contains most of Xcode’s default templates. iOS templates are in a different folder. You’ll look at those next.
Exploring iOS Templates
In Finder, go to Go ▸ Go to Folder… again. Then, enter the location of the default iOS templates as follows:
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Templates/Project Templates/iOS
Click Go.
You’ll see the iOS folder. Here, you’ll find templates for the iOS category in Xcode, as seen in this screenshot:
Open the Application subfolder in Finder, and then open App.xctemplate.
Take a look. When you create a new iOS app, Xcode uses this template. Next, you’ll copy this template to a different location so you can customize it and make it your own.
Creating Your First Project Template
Now it’s time to create your custom project template. There are three basic steps:
- Copy an existing template. In this case, App.xctemplate.
- Create a folder to store all your custom templates.
- Modify the copied template to match your needs.
First, while in the iOS default templates folder, right-click App.xctemplate and click Copy.
Next, open a new Finder window. Press Command-Shift-G or go to Go ▸ Go to Folder… and type in the path to the Xcode folder in your user library:
~/Library/Developer/Xcode
Then, click Go.
The Xcode folder stores device logs, application archives and other user data. Most importantly, for now, it also stores your custom templates. It is in your home directory and therefore is specific to your user account on the machine.
To create new custom template subfolder, right-click in this folder and then click New Folder. Name the subfolder Templates. Then, open the new Templates subfolder.
This folder will store your custom templates. Now, right-click in the folder and click Paste Item to paste the copied App.xctemplate.
Next, rename the item you just pasted from App.xctemplate to View Model App.xctemplate.
Your new template looks like this:
To make your template work, you’ll need to give it an unique identifier. Inside View Model App.xctemplate, open TemplateInfo.plist with Xcode.
Change the value for Identifier to com.raywenderlich.mvvm.
Voilà! :] You just created a new custom template!
However, you’ll need to add code to make it more meaningful. Xcode knows to read templates in the folder you just created. However if Xcode is already open, you need to quit and restart to enable your changes. Then, go to File ▸ New ▸ Project… to open the New Project dialog.
Scroll to the bottom of the iOS category. Here, you’ll find a new Templates section that holds your custom templates.
Select View Model App and click Next.
Your template comes with the same rich list of options as the default app template. How does a simple template exhibit all that behavior?
Contents of a Template
You’re now going to look at the template you just created to learn what makes up a template.
Go back to Finder and open View Model App.xctemplate.
Inside the template, you’ll find:
- TemplateInfo.plist: The file which is the driving force behind the template. This file contains the logic that determines what you’ll see in the New Project dialog and how to interpret any user input.
- Any other files that will be used to form the template project. Currently, you’ll only see Main.storyboard as that’s the only file necessary to kick off the project.
Usually, a SwiftUI app starts with a ContentView.swift file, but it’s missing from this template folder. That’s because this template inherits ContentView.swift indirectly from another template called iOS SwiftUI App.xctemplate, located in the same folder as the original App.xctemplate you copied.
App.xctemplate inherits from Core Data Cocoa Touch App.xctemplate, which in turn inherits from iOS SwiftUI App.xctemplate. It’s templates all the way down! :]
Now that you’ve reviewed the templates, you’ll create a template ViewModel.swift file and connect it with your custom template’s ContentView.swift. This will be the first customization of the template you’ll do to start seeing the power of custom templates!
Customizing the Project Template
The first step to customizing your project template is creating a view model class.
Open Terminal.app and enter the following:
touch ~/Library/Developer/Xcode/Templates/View\ Model\ App.xctemplate/ViewModel.swift
Note: Make sure you’ve created View Model App.xctemplate with the exact name and location as instructed, or the previous command won’t work.
This command creates an empty ViewModel.swift file within your template.
Next go back to Finder and open ViewModel.swift in Xcode. At the moment, it’s blank, so add the following code to its contents:
import Foundation
import Combine
class ViewModel: NSObject, ObservableObject {
@Published var title = "Hello, world! :]"
}
This creates a template ViewModel
class that will drive your app’s view behavior.
With the code above, you’ve done a few things:
At the top is the ___FILEHEADER___
text macro. This macro is defined by Xcode and populates the file with copyright information, including your name, the current date, and the file name. ___FILEHEADER___
looks like this:
// ___FILENAME___
// ___PACKAGENAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
// ___COPYRIGHT___
//
After ___FILEHEADER___
, the code added the necessary imports and defined the ViewModel
class containing a title
string for now.
Save and close ViewModel.swift.
Your template will now create ContentView.swift from the inheritance described earlier and also ViewModel.swift from the file you just added. But you haven’t done anything to tell your template to add these files to a new Xcode project. To do this you’ll dive into TemplateInfo.plist. First, take a look at what the TemplateInfo.plist contains.
Working With Project Template Info
TemplateInfo.plist is the brains of your template. It instructs the template how to connect with ViewModel.swift. Next, you’ll explore TemplateInfo.plist‘s contents and create those instructions.
Open TemplateInfo.plist with Xcode.
Below are some essential keys you’ll find inside the property list:
- Kind: The type of template. In this case it is
Xcode.Xcode3.ProjectTemplateUnitKind
, which means it’s a project template. - Identifier: A unique identifier for your template.
- Ancestors: A list of identifiers of other templates that this template inherits from. You’ll see two items in this array:
com.apple.dt.unit.coreDataCocoaTouchApplication
andcom.apple.dt.unit.sceneLifecycleApplication
. - Concrete: Indicates if the template is available directly while creating a new project in Xcode. Since you want to use this template to create a custom project, set it to
true
. If set tofalse
, you can’t use the template directly as it will be an abstract base template from which your other templates can inherit information. - Description: A short description of the template.
- SortOrder: Usually, Xcode arranges templates alphabetically, but you can set the sort order here to override the alphabetical sorting.
- NameOfInitialFileForEditor: Indicates the file that Xcode will display once the new project is ready.
- Options: A list of different dialog options in the New Project dialog.
Options
is the most complex as well as the most important of all the keys. UsingOptions
, you can ask the user for input in the form of text fields, checkboxes and drop-down lists. Additionally, theOptions
section determines how to interpret user input.
Next, you’ll learn more about these options so you’ll know what “options” you have when you’re building your custom template.
Exploring Options
Click the arrow to the left of Options to open it. Then, do the same to open Item 1.
This option allows users to choose the interface type for their projects. They can choose either SwiftUI or Storyboard.
You want to add ViewModel.swift to the project if the user chooses SwiftUI. You’ll do that shortly.
Here’s what’s inside the Item 1
dictionary:
- Identifier: A unique identifier to reference the option.
- Name: The name of the option that the user will see in the New Project dialog.
- Description: A short description of the option’s purpose.
- Values: A list of possible values for
popup
type options. - Default: A default value in case the user doesn’t choose one.
- Type: The type of the option, either
popup
,checkbox
ortext
.
Bravo! You just finished learning the anatomy of the brain of a template. It’s time to now feed in your custom code. :]
Adding the View Model
So far, you’ve used Xcode’s Property List Editor to navigate TemplateInfo.plist. Xcode is a great way to view and tweak property lists, but it can be tough to make larger changes. Instead, you’ll edit the raw XML.
Open TemplateInfo.plist in TextEdit.app or your favorite text editor. Then, near the bottom of the file, find Values. It’ll look like this:
Remove the Values
key as well as the array under it. Replace it with this:
Units
SwiftUI
Nodes
ViewModel.swift
Definitions
ViewModel.swift
Path
ViewModel.swift
Storyboard
Save the file and reopen it in Xcode. It will look like this:
Here’s what’s in the items above:
-
Units
is a list of values for the option, along with the behavior for each value. - Each item in the dictionary is a
Value
that the user will see in the New Project dialog. Currently, the values are SwiftUI and Storyboard, which are the same as the oldValues
. - Under the SwiftUI value, there’s a dictionary for your added behavior.
-
Nodes
is a list of files that will be included if the user chooses the SwiftUI value as the option foruserInterface
. -
Definitions
contains the actual path for any files inNodes
. - A
Storyboard
value that’s the same as the one in the oldValues
, without any behavior.
You’re finally ready to use your template to actually create your project!
Using the Template
Now, you’ll use the template you just set up. First, close Xcode, then reopen it for your changes to take effect.
Go to File ▸ New ▸ Project… to open the New Project dialog. Scroll to the bottom of the iOS category and select your View Model App template. Click Next.
On the next screen, set the following values for the options:
- Product Name: Stellar Space.
- Organization Identifier: com.raywenderlich.
- Interface: SwiftUI.
- Language: Swift.
Click Next, and then Create.
Build and run. You’ll be presented with the classic “Hello, world!” starter screen:
You’ll see the ViewModel.swift file in your project structure. However, ContentView
isn’t tied to ViewModel
yet. In ContentView.swift, add the following inside ContentView
, above body
:
@ObservedObject var viewModel = ViewModel()
Next, find Text
inside body
. Then, replace it with this:
Text(viewModel.title)
Build and run.
You’re now pulling the text from the view model.
You’ve created a project with your custom template and used the new ViewModel
. Next, you’ll flesh out the rest of Stellar Space.
Populating the Project
You’ll find most of the files for Stellar Space inside the project materials that you downloaded earlier.
Open the project materials in Finder and select the API and UI folders.
Then, drag these into Xcode’s Project navigator.
Select the Copy items if needed checkbox. Click Finish.
You’ve added a bunch of Swift files to Stellar Space. Later, you’ll use the files in the API folder to communicate to NASA’s API, and you’ll use the files in the UI folder to beautify your ContentView
.
Next, select Assets in Xcode’s Project navigator. Delete the empty AppIcon asset.
Finally, drag AppIcon.appiconset from the project materials into Assets, right next to AccentColor.
You’ve just created Stellar Space’s app icon.
Build and run.
Oh, no! Looks like there’s an APIError
object missing from Stellar Space. APIError
is an enum
that Stellar Space uses to identify a special group of errors. Don’t worry, because next, you’ll learn about Xcode file templates and create the APIError
enumeration in the process.
Creating File Templates
So far, you’ve worked with project templates that Xcode uses to create new projects. But Xcode also uses template files when you add a file to an existing project, so now you’ll work on customizing file templates.
Using file templates reduces the amount of boilerplate you need to write. Therefore, you are best to employee custom file templates when you find yourself often creating new files and writing almost identical boilerplate code to start the file off.
Everything you see in the New File dialog is a template, just like in the New Project dialog.
APIError
is an enum
, so you’ll create an Xcode template that makes a single Swift file with an enum
inside. That can be useful as you might often create enumerations and having a template to make life easier doing so will reduce your time spent writing boilerplate code.
To create a Swift Enum template for APIError
, you’ll copy and modify the default Swift File template.
Copying a Default File Template
Xcode stores its file templates alongside project templates. In Finder, go to Go ▸ Go to Folder… and type in the path to the default file templates:
/Applications/Xcode.app/Contents/Developer/Library/Xcode/Templates/File Templates
Click Go.
Open the MultiPlatform subfolder and then the Source folder within. Here, you’ll find the Swift File.xctemplate.
Swift File.xctemplate is the template that Xcode uses to create a plain Swift file.
It’s a simple template and a great starting point for your enum template as it’s very close to what you need. Copy Swift File.xctemplate to the location where you stored your custom templates:
~/Library/Developer/Xcode/Templates/
Next, within the custom templates folder, rename Swift File.xctemplate to Swift Enum.xctemplate. When you’re done, the custom templates folder will look like this:
Great! But it’s still the boilerplate Swift File template. So, now, you’ll customize it.
Customizing the File Template Info
You’ll use your new Swift Enum.xctemplate template to create APIError.swift, which contains an enum
with an Error
raw value. So, it’d be great if your template gave users the option to set a raw value for the enum when creating a file from the template. You’ll do that by adding an option to TemplateInfo.plist.
In Swift Enum.xctemplate, open TemplateInfo.plist with Xcode. Change the values for the following keys:
- Description: A Swift enum with a raw value.
- Summary: A Swift enum with a raw value.
- DefaultCompletionName: Enum.
Description
and Summary
are used by Xcode in the same way as project templates. However, DefaultCompletionName
is new. The value of this key will be the name of any file you create if you don’t rename it. In other words, without changing the file name, the template would name a new file Enum.swift.
At this stage, TemplateInfo.plist looks like this:
Now you need to include an option to ask the user to enter a raw value for the enum, such as Error
. This will be user input that’s used while generating the new file from the template.
To do so, open TemplateInfo.plist in TextEdit.app or any other text editor. Near the top of the file, after the very first
, add the following:
Options
Type
text
Description
Raw value for enumeration
Name
Raw Value:
Required
Identifier
rawValue
Your TemplateInfo.plist will look like this when you’re done:
You’ve added a new Options
array with one dictionary inside it. This will tell Xcode to provide an option when using the template. In this case, the single option added represents a Raw Value option to choose a raw value for the enumeration. Here’s what each item in the dictionary means:
- Type: The type of user input:
text
,checkbox
orpopup
. - Description: A short description of the option.
- Name: The name of the option that the user will see in the New File dialog.
- Required: A boolean value to decide whether or not the user needs to enter a value for the option before continuing.
- Identifier: A unique identifier to reference the value of the option.
You’re done adding file template info. Now, you need to tell the template how to create the Swift file in the way you need.
Creating Template Files
Inside Swift Enum.xctemplate, open FILEBASENAME.swift. This is the file that your file template creates in your project.
___FILEBASENAME___
is a text macro that all file templates have. It’s the name that you choose for the file in the New File dialog, just without the file extension. For example, if you choose APIError.swift as the file name when using the template, then Xcode sets ___FILEBASENAME___
to APIError
.
Inside FILEBASENAME.swift, replace the contents with this:
//___FILEHEADER___
import Foundation
enum ___FILEBASENAMEASIDENTIFIER___: ___VARIABLE_rawValue___ {
case one
case two
}
You’ve created a simple enum with two default cases. In this file, you use three template macros. Here’s what each of these do.
At the top of the file is ___FILEHEADER___
, which sets the usual copyright, file and author details.
Next, instead of a name for the enum, you have ___FILEBASENAMEASIDENTIFIER___
. This is the same as the ___FILEBASENAME___
variable for the file name, except that you can’t use ___FILEBASENAME___
inside the template file.
Finally, ___VARIABLE_rawValue___
is a reference to the rawValue
option you created earlier. The format looks like ___VARIABLE_
.
That’s all you need to set up your template. Quit and reopen Xcode. Next, you’ll use your template to create APIError.swift.
Using the Template
In Xcode, with Stellar Space open, go to File ▸ New ▸ File…. Then, choose your Swift Enum template.
For Raw Value, enter Error. Then, click Next.
Name the file APIError.swift and click Create.
Open APIError.swiftcases within APIError
and replace them with this:
case network(description: String)
case parsing(description: String)
Finally, build and run.
The app compiles! But, you still have some finishing touches to add to ContentView
and ViewModel
to display more than just a fancy message on the screen. :]
Finishing the Project
On the final stretch now to finish off the project to display those lovely NASA images.
In Xcode, open ViewModel.swift. First, add the following import at the top of the file:
import UIKit
@Published var image: UIImage?
@Published var errorMessage: String?
@Published var isLoading = true
@Published var date = Date()
var canMoveForward: Bool {
!calendar.isDateInToday(date)
}
var dateString: String {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .medium
dateFormatter.timeStyle = .none
return dateFormatter.string(from: date)
}
private let spaceImageFetcher = SpaceImageFetcher()
private let calendar = Calendar.current
private var disposables = Set()
func changeDate(days: Int) {
if let newDate = calendar.date(byAdding: .day, value: days, to: date) {
date = newDate
loadImage()
}
}
func loadImage() {
isLoading = true
image = nil
errorMessage = nil
spaceImageFetcher.dailyPicture(date: date)
.receive(on: DispatchQueue.main)
.flatMap { dailyPic -> AnyPublisher in
self.title = dailyPic.title
return self.spaceImageFetcher.loadImage(url: dailyPic.url)
}
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { [weak self] value in
switch value {
case .failure(let reason):
print("Failure: \(reason.localizedDescription)")
self?.errorMessage = "Something went wrong."
case .finished:
break
}
self?.isLoading = false
}, receiveValue: { image in
self.image = image
})
.store(in: &disposables)
}
I won’t explain the code here because it’s not relevant to the tutorial. All you need to know is that this code adds the logic for accessing the NASA API and displaying images.
Then, open ContentView.swift. Replace the declaration of body
with this:
var body: some View {
ZStack {
ColorPalette.primary.ignoresSafeArea()
VStack {
Text(viewModel.dateString)
.foregroundColor(ColorPalette.accent)
Spacer()
if !viewModel.isLoading {
if let image = viewModel.image {
Image(uiImage: image)
.resizable()
.scaledToFit()
Text(viewModel.title)
.foregroundColor(ColorPalette.accent)
} else if let error = viewModel.errorMessage {
Text(error)
.foregroundColor(ColorPalette.secondary)
}
Spacer()
ZStack {
HStack {
Button("Back", action: changeDateBack)
.padding(.leading, 20)
Spacer()
if viewModel.canMoveForward {
Button("Forward", action: changeDateForward)
.disabled(!viewModel.canMoveForward)
.padding(.trailing, 20)
}
}
}
} else {
ProgressView()
.progressViewStyle(ProgressViewWithBackgroundStyle())
.frame(width: 100, height: 100)
Spacer()
}
}
}
.onAppear(perform: fetchImage)
.buttonStyle(SpaceButtonStyle())
}
func changeDateBack() {
viewModel.changeDate(days: -1)
}
func changeDateForward() {
viewModel.changeDate(days: 1)
}
func fetchImage() {
viewModel.loadImage()
}
With this, ContentView
has the UI to display everything happening in ViewModel
. Build and run.
[图片上传中...(image-564458-1642558611777-0)]
At last, you’ve completed Stellar Space, and you have two new Xcode templates to show for it: a project template and a file template!
Where to Go From Here?
In this tutorial, you created both a project template and a file template, using Xcode’s default templates as a source of inspiration.
You added your files to your templates, controlled them with the Template Options file and customized them with variables.
Templates are a great example of the power of an IDE like Xcode. If you’d like to learn more about how Xcode can make your life easier, iOS App Distribution & Best Practices dives under the hood of the Xcode project structure.
Your project template separates business logic from UI code by including a view model. If you’d like to learn more about project architecture, have a look at Advanced iOS App Architecture. You might get some ideas for new templates, too!
Please join the discussion below if you have questions or comments or want to share some of your creative templates.