Tuist Template: What is it and how to create them
Table of Contents
Part 1 in the series "Tuist templates and how to use them". In the first part, we will discuss how to create a template file.
What is a template
After initial set up with Tuist, it is relatively easy to manage your project. But having to create folders and files manually every time is not very convenient. It would be good if we can extract some boilerplate code and reuse it. So we don't have to create everything from the ground up manually. That's what the template is for.
The template doesn't benefit just at the beginning of the project, but projects with an established architecture. You might want to bootstrap new components or features that are consistent with the project. For example, you can generate VIPER components from the template files.
You can easily support sarunw.com by checking out this sponsor.
Debug 10x faster with Proxyman: Your ultimate tool to capture HTTPs requests/ responses, natively built for your iPhone and macOS. Special deal for Black Friday: Get 30% off for all Proxyman licenses with code “BLACKFRIDAY2024”.
How to create a template
To create templates, we need two things:
- A new directory named after the name of the template you want,
name_of_template
. We put this directory under theTuist/Templates
directory. - A manifest file,
name_of_template.swift
that describes the template.
I will create a new template called sarunw, so I create a new directory, sarunw
, with a manifest file sarunw.swift
. Here is what our file structure looks like.
Tuist
- Templates
- sarunw
- sarunw.swift
And this is how the manifest file looks like:
sarunw.swift
import ProjectDescription
let template = Template(
description: "Custom template",
attributes: [
.required("name")
],
files: [
.string(path: "README.md",
contents: "# Welcome to Tuist")
]
)
Template object
The manifest file (sarunw.swift
) requires us to declare one thing, a Template object. Template consists of three properties.
- description: describe our template. It shows up when you run
tuist scaffold list
, a command to show all available templates. - attributes: An array of parameters for our template. The parameter can either be required (
.required
) or optional (.optional
) type. - files: an array of our generators. We have two ways of generating files.
-
public static func string(path: String, contents: String)
.string
: this will put the stringcontents
to destination pathpath
. In my example, it will create aREADME.me
file with a content of# \(name_attribute)
. -
public static func file(path: String, templatePath: ProjectDescription.Path)
.file
: this will copy file fromtemplatePath
over to destination path (path
). You can also use Stencil templating language here.
-
Use a template
We have two ways to use a template which I won't focus on in this article. For the testing purpose, we will use the tuist scaffold [template_name] --parameter_name argument
to generate the files.
Run tuist scaffold sarunw --name foo
, and Tuist will generate one README.md file with the following content.
# Welcome to Tuist
The line that responsible for this generation is this.
.string(path: "README.md",
contents: "# Welcome to Tuist")
Reading parameters
attributes
is a list of parameters that we can use in our template. In the previous, we declare one required parameter, name, but we didn't use it anywhere. To use it, you refer to it by name using Template.Attribute
.
Change our manifest file to this.
import ProjectDescription
let template = Template(
description: "Custom template",
attributes: [
.required("name")
],
files: [
.string(path: "README.md",
contents: "# \(Template.Attribute.required("name"))")
]
)
Run tuist scaffold sarunw --name foo
, and Tuist will generate one README.md file with the following content.
# foo
As you can see, this time, our README.md is populated with the name parameter.
You can make the code cleaner by extract the parameter to a variable. The following code will produce the same effect but a little bit cleaner.
import ProjectDescription
let nameAttribute: Template.Attribute = .required("name") // 1
let template = Template(
description: "Custom template",
attributes: [
nameAttribute // 2
],
files: [
.string(path: "README.md",
contents: "# \(nameAttribute)") // 3
]
)
<1> We extract parameter into a variable and use it in <2> and <3>.
Static template
So far, I only show you one way to generate static content with .string
, which might not be very useful in real life. Luckily, that is not the only way. We can also copy a file over with .file
.
We will add a new file, gitignore
, that will use as a template file to copy over. I will put it at the same level as our manifest file, but it doesn't need to be (just make sure you reference it correctly).
I create a new gitignore file that only excludes the only xcodeproj since we don't need it anymore with Tuist. I don't prefix my file with a dot (.
), so it visible in the Finder.
gitignore
*.xcodeproj
Here is our template files.
Tuist
- Templates
- sarunw
- sarunw.swift
- gitignore
Then, we modify our manifest to copy the file.
import ProjectDescription
let nameAttribute: Template.Attribute = .required("name")
let template = Template(
description: "Custom template",
attributes: [
nameAttribute
],
files: [
.string(path: "README.md",
contents: "# \(nameAttribute)"),
.file(path: ".gitignore", templatePath: "gitignore") // <1>
]
)
<1> We copy the gitignore
to .gitignore
. The source and destination file name doesn't have to match.
Run tuist scaffold sarunw --name foo
again. This time Tuist will generate two files, README.md
and .gitignore
.
README.md
.gitignore
Tuist
- Templates
- sarunw
- sarunw.swift
- gitignore
Dynamic template
If .string
and .files
don’t provide enough flexibility, you can use the Stencil templating language via the .file
case. Besides that, you can also use additional filters defined here.
You might want to use a dynamic template when your template supports different implementations based on template argument.
For example, I will generate different README.md based on the name provided.
To use a dynamic template, we need to change .string
to .file
.
From:
.string(path: "README.md",
contents: "# \(nameAttribute)")
To:
.file(path: "README.md", templatePath: "README.stencil")
Then we create a new README.stencil
. You can learn Stencil syntax at their website.
# Welcome {{ name | capitalize }}
{% if name != "sarunw" %}
Nice to meet you!
{% endif %}
The above file will put a capitalize name in the welcome header, and if your name isn't sarunw
, it will show a greeting message.
The final manifest file (sarunw.swift) look like this:
import ProjectDescription
let nameAttribute: Template.Attribute = .required("name")
let template = Template(
description: "Custom template",
attributes: [
nameAttribute
],
files: [
.file(path: "README.md", templatePath: "README.stencil")
]
)
Example 1: Alice
tuist scaffold sarunw --name Alice
Use a template with Alice as a name will produce the following README.md content.
# Welcome Alice
Nice to meet you!
Example 2: Sarunw
tuist scaffold sarunw --name sarunw
Use template with sarunw as a name will produce the following README.md content.
# Welcome Sarunw
You can easily support sarunw.com by checking out this sponsor.
Debug 10x faster with Proxyman: Your ultimate tool to capture HTTPs requests/ responses, natively built for your iPhone and macOS. Special deal for Black Friday: Get 30% off for all Proxyman licenses with code “BLACKFRIDAY2024”.
Conclusion
You have learned everything you need to create a Tuist's template. I don't cover the detail on how to use the template. So far in this article, you know one way to use a template using tuist scaffold
.
In the next article, I will go through different ways of using a template in detail.
Read more article about Tuist, Template, Xcode, Development, or see all available topic
Enjoy the read?
If you enjoy this article, you can subscribe to the weekly newsletter.
Every Friday, you'll get a quick recap of all articles and tips posted on this site. No strings attached. Unsubscribe anytime.
Feel free to follow me on Twitter and ask your questions related to this post. Thanks for reading and see you next time.
If you enjoy my writing, please check out my Patreon https://www.patreon.com/sarunw and become my supporter. Sharing the article is also greatly appreciated.
Become a patron Buy me a coffee Tweet ShareUsing App Store Connect API with Fastlane Match
Apple announced the App Store Connect API back in WWDC18. It provides an official way to interact with App Store Connect, and Fastlane already supports this. With a recent 2FA enforcement from Apple, it is time for you to adopt it.
Tuist init: How to use Tuist templates to bootstrap your project
Learn how to use, and limitations of tuist init, a command that bootstrap a new project.