Let's Work Together

How to Automate Swift Boilerplate Code with Sourcery

Image by Brandon Wever

Swift has greatly improved the lives of iOS and macOS developers, but it’s missing features to automate away boilerplate code. That’s why I’m excited about a new project called Sourcery, which is a Swift code generation tool.

Switching between our Android projects written in Java (or more recently, Kotlin) and iOS projects in Swift makes me sorely miss annotation processing. Annotations in Java can be used to generate common code and things that Swift developers would have to hand write. Hand written code has to be bug free and consistently updated and maintained. Things like implementing equals and parsing JSON typically require boilerplate code, unique enough that you cannot abstract it away.

Although Swift does not have annotation processing, we can get pretty close with Sourcery. Let’s take a look at how it all works in this quick Sourcery tutorial.

Meet Sourcery

Sourcery is a code generation library by Krzysztof Zabłocki that uses Stencil templates to generate Swift source code. Sourcery leverages marker protocols and annotations in comments to generate code. Along with these powerful tools there are also some sample templates in the repo that show you how you can generate the Equatable protocol and other useful code templates.

Running Sourcery

To run Sourcery you can either install the CLI from Github or add it to your project via CocoaPods with

1
pod 'Sourcery' 

and adding

1
$PODS_ROOT/Sourcery/bin/sourcery --sources {source} --templates {templates} --output {output}

to a Run Script Build Phase before Compile Sources.

Generating Code From Templates

A simple Sourcery Template called ListTypes.stencil looks like this:

1
2
3
4
5
// MARK - List of all Swift types in Sources directory

{% for type in types.all %}
  {{ type.name }}
{% endfor %}

Anything inside {% %} is considered control flow, anything inside of {{ }} is accessing and printing a value, and anything else is just printed as plaintext. The above will create a Swift file called ListTypes.generated.swift that looks like this:

1
2
3
4
5
6
7
8
// Generated using Sourcery 0.5.9 — https://github.com/krzysztofzablocki/Sourcery
// DO NOT EDIT

// MARK - List of all Swift types in Sources directory

  Account
	...Other types omitted for brevity
  User

Listing types may not be incredibly useful since it won’t even compile, so let’s look at a template that does something more useful. Imagine you want an instance method on certain types that will wrap the instance in a Dictionary.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// MARK - toDictionary functions

{% for type in types.all %}
{% if type.variables|annotated:"dictKey" %}
// MARK - {{ type.name }} toDictionary
extension {{ type.name }} {
  func toDictionary() -> [String: Any] {
    var dict = [String: Any]()
{% for var in type.variables|instance|annotated:"dictKey" %}
    dict["{{ var.annotations.dictKey }}"] = {{ var.name }}
{% endfor %}
    return dict
  }
}
{% endif %}
{% endfor %}

We see a lot of new constructs in this template such as control flow with if and using annotated. If you’re familiar with Java, annotations might be near and dear to you. If not, here’s what they look like:

1
2
3
4
5
6
7
8
struct Post {
    // sourcery: dictKey = "id"
    let id: String
    // sourcery: dictKey = "title"
    let title: String
    // sourcery: dictKey = "description"
    let description: String
}

Here we are describing a Post model with a few fields. Since we have commented above each field with the special tag sourcery: , we can access the value of dictKey in our templates. Using this we can create more powerful code generation as it allows the programmer to do metaprogramming. The code that is generated would look like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// Generated using Sourcery 0.5.9 — https://github.com/krzysztofzablocki/Sourcery
// DO NOT EDIT

// MARK - toDictionary functions

// MARK - Post toDictionary
extension Post {
  func toDictionary() -> [String: Any] {
    var dict = [String: Any]()
    dict["id"] = id
    dict["title"] = title
    dict["description"] = description
    return dict
  }
}

As you can see, the code that the developer would have to hand write is very repetitive and prone to such errors as typos. The template saves time because you would have to write this function for any struct where you would want to implement the toDictionary function. Additionally, if you ever update the Post type with new fields, you would not have to remember to update the toDictionary function as Sourcery would generate it for you.

I won’t get into anything more complicated in this post, but I will say that finding things to automate is addicting and usually saves time in the long run. If you find yourself writing boilerplate in Swift often, see if you can automate it with Sourcery.

Check out the Github Page for more information about integrating Sourcery in your own project.