Demystifying Code Generation On iOS

This blog will give you an insight into the basics of code generation and relevant tools.

Demystifying Code Generation On iOS

By Akshay Hegde

This article is aimed at helping you learn the basics and understanding the tools for generating code. This is geared towards Swift/iOS developers. However, the underlying concepts of generating code remain the same irrespective of the programming language.

Let’s address this first: Why generate code when you can write it?

Boilerplate code

Often, code is written for standard protocol generations such as implementing Equality or Codable for model classes.

One may want to create enums and structs and avoid string-based APIs for your resources.

Consistency and productivity

The generated code will always look the same irrespective of who generated it or when. The same conventions and code styling can be maintained. Changing the rules and conventions is easier, as you can make the change in the code generation logic and all that code can just be generated again with no effort.

Single source of truth and ease of portability

The code generation logic can be written in such a way that porting to a different language and generating code is simple. The logic of code generation remains the same, just the output can be any language, swift, Kotlin, or Dart.

This allows us to keep some shared logic and a single source of truth of the data that results in the code for any platform. For instance, At Gojek, we use the same JSON file to generate all the theme classes in iOS and Flutter.

This theme is the basis that allows us to style according to the Asphalt Aloha Design system

The basics

The premise of generating code can be thought of as:

Input + Templates (Written in Templating Language) = Output

Input can be a JSON schema, code or annotations in code, or anything that has the information for which you want to generate the code.

Templates are just plain text with “placeholders” that allow you to “fill in the blanks” with the data from the input

Templating languages allow you to consume input, transform them and fill them in those templates.

Output is always plain Text.

Learn with a simple example

At Gojek, we have design system tokens shared in a JSON format that are ported from Figma.

Now we want code to be generated from this JSON, because we would not want to write code every time tokens are modified or when a new theme is introduced.

Consider an example JSON structure of colors:

Here the input is a JSON.

Our code generation logic is simple, for every color token/ key in the JSON we want to write a var someColorToken: ColorToken { get } in a protocol.

So we write a template. For Swift, you can use Stencil. The reason we are using this is that it comes with some nice built-in functions to write or transform the input specific to Swift. There is SwiftStencilKit that given even more built-in filters and transformers that make our job easier.

This is our template, Written to ColorTokens.stencil file.

The steps are:

  1. Read from the theme JSON and create an array of all keys.
  2. Pass this Array and the above template to stencil.
  3. Generate.

Let’s look at the template a bit more closely. Note that everything is just text, a protocol skeleton that is written in swift.

This is just a for loop that loops over the Array we just passed in by reading the JSON. {% for name in names %} ... {% endfor %}

This is transforming each of that name/key into a variable name;{{name|snakeToCamelCase|lowerFirstLetter}} Here a fill_active_primary becomes filleActivePrimary

To recap, the template just some text written in any language/syntax you want, they have “placeholders” that are filled from reading and transforming the input.

After calling try template.render(context), the string output is written Colortokens.swift file.

Code as the input

Take the popular example of Sourcery and let’s look at the example of how it generates Equatable.

Sourcery depends on reading annotations in code. We parse this code, pass them to stencil as an input that will be used in the template to “fill in the blanks”.

How do I read swift code and pass it to Stencil?

  1. Swift Syntax
  2. SourceKit / SourceKitten

The above tools enable you to parse and get syntax information or the structure of the swift code that you can use for further processing. For example as a JSON.

Code generation for Resources

We have .string and plist files for localization, we have JSON files for every item in the asset catalog.

So with the same principle, we can read/parse these files, write some templates and generate code.

That’s exactly what SwiftGen does.

Finally…

There are a lot more examples out there for code generation. e.g., Mockolo generates mocks by reading annotated classes with Swift-Syntax.

Stencil is not the only templating language. {{ mustache }} is more widely used and available for many other languages. Stencil follows a very similar syntax to mustache.

For instance, SwiftMockGeneratorForXcode is an Xcode plugin that to generates mocks and stubs for swift classes uses SourceKit and Mustache.

Note that in all these cases the fundamental concept remains the same.

Now that you know the basics process of how code generation works, I hope you start recognizing parts of your own project code that can just be generated instead of writing them. 🙌

Want to read more stories? Click here.

Oh, we’re hiring! Check out open job positions here: