Engineering

How to use Sourcery to speed up your Swift development

While developers are enjoying the refactoring tools in Xcode 9 and using the new Swift 4 APIs to streamline their iOS development, some time-consuming tasks remain. The less dynamic approach of Swift, compared to Objective-C, is still a heated debate in the community. Some developers decided to take a different approach to accelerating their productivity with Swift: metaprogramming.

In this post, we’ll talk about the advantages of code generation and how Sourcery, a tool created by Krzysztof Zabłocki, helps you automate the redundant part of Swift coding, and allows you focus on writing code specific to your app.

What is metaprogramming?

Metaprogramming means a lot of things and every language has its own definition. At a language-agnostic level, metaprogramming is a program treating another program like data, hence the meta. Once you have that data, you can use it to modify your application. The Scala programming language has support for metaprogramming built in with something called « macros ». At compile time, it is possible to generate additional code and that’s code the developer doesn’t have to write.

While Swift doesn’t have support for metaprogramming built in, it has amazing contributor to the open source ecosystem who made tools such as sourcekitten, Stencil and the one we care about today, Sourcery.

What is Sourcery

Sourcery is a metaprogramming tool that helps you generate code based on template files. Let’s see how It works and how you can use it, so that you no longer have to write an Equatable implementation ever again.

When Apple introduced Swift, they also introduced a set of tools to enable all of the features we’d expect with a new language: auto-completion, syntax highlighting, and more recently, refactoring! This tool is called SourceKit and runs as a background service. Thanks to this tool, JP Simard, and Sourcekitten, it becomes possible to know all of the types that you have — all the classes, all the enums, everything!

Sourcery‘s command line tool communicates with SourceKit to analyze your source code and expose it to templates that generate additional Swift code. The advantages may not be obvious right away, so let’s see an example.

Using Sourcery

The first thing you need to do is to install the Sourcery command line tool. Once again, the easiest way to do that is to use homebrew:

brew install sourcery  
sourcery --version # prints 0.7.2  

Comparing two builds from the buddybuild public API

For the purpose of this example, let’s say that you’re using Swift to communicate with the buddybuild public API, to fetch information about your latest builds. The best way to represent a build is a struct:

struct Build {  
    let id: String
    let createdAt: Date
    let status: String
}

In our application, we want to be able to compare two builds and Swift makes it quite easy, using the Equatable protocol:

extension Build: Equatable {  
    static func ==(lhs: Build, rhs: Build) -> Bool {
        return lhs.id == rhs.id
            && lhs.createdAt == rhs.createdAt
            && lhs.status == rhs.status
    }
}

Once your protocol is implemented, then it becomes easy to compare two builds using the == operator, as you would expect. The problem is, if you look at our public API, there is a lot more than id, date, and status that we expose in the response. We also expose the commit, how the build was triggered, and the links to download or install the artifacts. Even if we don’t need this information right now, we probably will at some point in the development of our project. And, each time we handle new fields from the JSON payload, we’ll have to update our implementation of Equatable.

This is where Sourcery comes in handy: it generates these implementations for you.

Setting up Sourcery in our project

The Sourcery repository comes with sample templates, one of those showing how to use it to implement Equatable automatically. We want to use it in our own repository so we’ll just create a Templates folder and download it there.

mkdir Templates  
curl -o Templates/AutoEquatable.stencil \  
https://raw.githubusercontent.com/krzysztofzablocki/Sourcery/master/Templates/Templates/AutoEquatable.stencil  

Now that we have our templates, let’s open it to see how It works.

// MARK: - AutoEquatable for classes, protocols, structs
{% for type in types.implementing.AutoEquatable|!enum %}
// MARK: - {{ type.name }} AutoEquatable
{% if not type.kind == "protocol" %}extension {{ type.name }}: Equatable {}{% endif %}
{{ type.accessLevel }} func == (lhs: {{ type.name }}, rhs: {{ type.name }}) -> Bool {
    {% if not type.kind == "protocol" %}
    {% call compareVariables type.storedVariables %}
    {% else %}
    {% call compareVariables type.allVariables %}
    {% endif %}
    return true
}
{% endfor %}

As you can see, Sourcery generates an implementation of the Equatable protocol for any struct or class that implements the dummy protocol AutoEquatable. Let’s go back to Xcode and create an empty implementation in a Sourcery.swift file. Then, we’ll go back to our Build struct and implement this protocol.

Editorial Note: the approach outlined above is the default approach — and probably the safest — because it’s explicit. You can customize the template to use different criteria.

Now that we have everything setup, let’s run the sourcery command line tool in our repository.

sourcery --templates Templates --sources Dumbbell --output Dumbbell/Generated  

Now, if you look in the generated/ folder, you should see a new Swift file containing an implementation of the Equatable protocol:

// MARK: - AutoEquatable for classes, protocols, structs
// MARK: - Build AutoEquatable
extension Build: Equatable {}  
public func == (lhs: Build, rhs: Build) -> Bool {  
    guard lhs.id == rhs.id else { return false }
    guard lhs.createdAt == rhs.createdAt else { return false }
    guard lhs.buildStatus == rhs.buildStatus else { return false }
    return true
}

Next, let’s go back to Xcode to add this file to your project.

Adding the generated file to Xcode

Then, delete your own implementation of the Equatable protocol that we wrote earlier.

That’s it! Now, every time you update your Build model or any other object that implements the AutoEquatable protocol, run the sourcery command to update the implementations. Even better, sourcery comes with a watch mode that you can activate to rebuild everything automatically during development.

Being deterministic: using a configuration file

One last thing you may want to do is to use a Sourcery configuration file. Using a configuration file lets you run sourcery with no additional parameters, meaning that everyone on your team can run it the same way. To achieve this, you need to create a sourcery.yml file at the root of your repository that contains the following:

sources:  
    - Dumbbell
templates:  
    - Templates
output: Dumbbell/Generated  

Using Sourcery and buddybuild

Probably one of the most heated debates in the iOS community is about Cocoapods. More specifically, whether or not to commit the Pods folder when building your application. According to our statistics, teams seem to be fairly divided on this topic (roughly half of teams who use buddybuild commit their Pods folder, while the others do not).

Editorial Note: If you’re interested in learning more about how development teams build their applications, stay tuned — we’re in the midst of putting together a series on the current state of mobile development!

Teams are faced with the same decision when it comes to deciding whether or not to share generated code: should you commit the generated implementation or should you ignore the generated/ folder altogether? There are pros and cons, but if you decide to “ignore”, buddybuild lets you run sourcery before the build happens. To achieve this, create a buddybuild_postclone.sh file that contains the following:

#!/usr/bin/env bash

brew install sourcery

sourcery  

Once you’ve committed and pushed this custom build step into your repository, buddybuild picks it up and runs sourcery before the build even starts. That is one of the many things you can achieve in buddybuild with Custom Build Steps!

Where do we go from here

There are many other things you can achieve with Sourcery to help streamline your Swift development. Since it exposes all of the types from your code, there are virtually limitless ways you can apply meta-programming to cut down the amount of redundant code that you need to write for your projects. Some developers are using it to generate mocks from protocols that they can then use in tests. Others are using it to implement JSON serialization. If you’re using Sourcery in your Swift projects, we’d love to hear how it’s helped you optimize your workflow. Feel free to reach us @buddybuild on Twitter, or by emailing team@buddybuild.com.

If you’re looking for other helpful tools to help streamline your Swift development, feel free to try buddybuild. Thousands of development teams trust buddybuild to reliably build, test, and deploy their Swift applications so that they can stay focused on creating awesome applications. Start your free trial today so you too can focus on crafting applications your users love.

Subscribe to receive other buddybuild updates.