Practical Sourcery examples

🚀
You're about to read an article that was previously reserved only for our premium supporters. Want to get access to valuable insights, cutting-edge tutorials, and time-saving code snippets immediately when they are published?

By becoming a paid supporter, you'll not only receive the latest content straight to your inbox, but you'll also contribute to the growth and maintenance of over 20 open-source projects used by 80,000+ teams—including industry giants like Apple, Disney, Airbnb, and more.

Join here

Even though my Sourcery has become the industry standard for anything related to metaprogramming in Swift, many people are still not fully leveraging its power. Many projects use it for primary use-cases like generating Mocks.

Larger projects like Airbnb, The New York Times, or The Browser Company use custom templates to enable workflows and architectures that are impossible to achieve in standard Swift.

I'll cover many more exciting Sourcery use-cases in future articles but let's start by giving you a few examples of what you probably didn't consider using Sourcery.

Sourcery AST primer

We usually access AST via one of the top-level objects Sourcery exposes. Let's quickly go over the ones we will be using in this article:

  • types <- Types in the codebase, available via custom accessors
    • all <- lists all types except protocols
    • protocols
    • classes/structs/enums <- filter to specific kind of type
    • based <- Types based on any other type, grouped by its name, even if they are not known. types.based.MyType returns a list of types based on MyType
    • implementing/inheriting <- similar to based but for known types (those defined in the codebase)
  • type <- access all types by their name

Examples

1. Finding all classes that could be final

The algorithm for this is straightforward:

  1. For all classes in the codebase
  2. Filter out those that aren't final or open
  3. If the type doesn't serve as a base for other types it can be marked final

What's interesting, though, is that Sourcery can generate anything, not just Swift code, so we can step further than just finding the classes: we can create a bash script that will rewrite your codebase.

  1. Find the class definition via grep git grep -lz 'class <%= type.name %>'
  2. Pipe it to perl regex replacement | xargs -0 perl -i'' -pE "s/class <%= type.name %>(?=\s|:)/final class <%= type.name %>/g"

Full template

2. Linting

For The Browser Company, I've implemented a lot of custom dev tooling, and I wanted to inform engineers if they didn't leverage it in their PRs.

One example is my LifetimeTracker. I want all view controller subclasses to conform to it. Standard linting isn't enough to verify this accurately since someone can do to the protocol via extension and/or in separate files from the type definition.

Sourcery can list the types that don't conform to a specific protocol:

{% for type in types.classes|based:"NSViewController"|!implements:"LifetimeTrackable" %}
// - {{ type.name }}
{% endfor %}

I add this to the project, and the linter part becomes relatively easy. I verify if the number of lines in a given PR has increased. If so, it means a class was added that didn't conform but should.

3. Finding classes conforming to a protocol

Back in Objective-C, you could leverage runtime to find all classes that conformed to a specific protocol, this functionality doesn't work in standard Swift, but with Sourcery, you can do even more.

Many bigger codebases (e.g., Airbnb) use a pattern where they find all types conforming to a given protocol and automatically register them in the application target.

func registerAllComponents() {
  {% for type in types.based.Component|!protocol %}
    ComponentsRegistry.register({{type.name}}.self)
  {% endfor %}
}

Any time the developers add a new Component, it will immediately register in the application without any manual code changes.

Conclusion

These are just a couple of examples of non-standard Sourcery use-cases. Sourcery get's even more powerful with Sourcery Pro's Xcode extension and it's powerful editor.

In the future, I'll cover specific use-cases in-depth, and you'll be able to see some real magic, in the meantime if you have any questions about Sourcery or your use-cases, I'm always happy to help.

You've successfully subscribed to Krzysztof Zabłocki
Great! Next, complete checkout to get full access to all premium content.
Error! Could not sign up. invalid link.
Welcome back! You've successfully signed in.
Error! Could not sign in. Please try again.
Success! Your account is fully activated, you now have access to all content.
Error! Stripe checkout failed.
Success! Your billing info is updated.
Error! Billing info update failed.