Tuist Template: What is it and how to create them

⋅ 6 min read ⋅ Tuist Template Xcode Development

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.

  1. Template
  2. Tuist Init
  3. Tuist Scaffold
  4. What is the different between Tuist init and scaffold

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.

Sponsor sarunw.com and reach thousands of iOS developers.

How to create a template

To create templates, we need two things:

  1. A new directory named after the name of the template you want, name_of_template. We put this directory under the Tuist/Templates directory.
  2. 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 string contents to destination path path. In my example, it will create a README.me file with a content of # \(name_attribute).

    • public static func file(path: String, templatePath: ProjectDescription.Path)

      .file: this will copy file from templatePath 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.

Sponsor sarunw.com and reach thousands of iOS developers.

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 Share
Previous
Using 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.

Next
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.

← Home