Generating Swift Code from a Data Source with Stencil CLI

May 18, 2020

In a previous company I worked for, we decided to consolidate the event tracking on all platforms. To do that, we created a YAML file describing all events we wanted to track. The description of an event would look similar to this:

user_sign_up_completed:
  name: User Sign Up Completed
  category: User
  description: An event that fires when the user account is created.
  event_specific_parameters:
    - parameter_name: authentication_method
      description: Authentication method used
      type: string
      allowed:
        - Facebook
        - Google
        - Email
        - Apple

In the iOS platform, to track this event, we would write the following code:

let event = TrackingEvent.userSignUpCompleted(authenticationMethod: .email)
eventTracker.track(event)

Imagine this was the TrackingEvent code:

struct TrackingEvent {
  let name: String
  let category: String
  let params: [String: String]
}

And imagine this was how we create an user_sign_up_completed event:

extension TrackingEvent {
  enum UserSignUpCompletedAuthenticationMethod: String {
    case facebook = "Facebook"
    case google = "Google"
    case email = "Email"
    case apple = "Apple"
  }

  func userSignUpCompleted(authenticationMethod: UserSignUpCompletedAuthenticationMethod) -> TrackingEvent {
    TrackingEvent(
      name: "User Sign Up Completed",
      category: "User",
      params: [
        "authentication_method": authenticationMethod.rawValue
      ]
    )
  }
}

At first, it might not look like a lot of work, but when we grow the file to hundreds of event definitions with multiple people updating the file every week, keeping the codebase in sync with the data source just becomes an impossible task.

Since it’s not feasible to do this work manually, we turned to code generation. On the iOS platform, we were already writing Stencil templates for SwiftGen and Sourcery, so it would be great if we could use Stencil for this case too.

Initially, I thought we could just pass the YAML file and the stencil template to a command-line tool, and we would get our new file generated, but that wasn’t the case. Since Stencil is a library, we actually need to assemble a program and use it as a dependency.

Stencil CLI

After going through this, I assembled a small CLI tool to do precisely that. You can install stencil-cli via homebrew:

brew install alephao/formulae/stencil

Then you can use the command stencil render to render a stencil template:

stencil render -t path/to/template.stencil -d path/to/datasource.yaml -o path/to/output.swift

Check out the repo alephao/Stencil-CLI for more information.

Final Words

I hope Stencil CLI can be helpful to you if you ever need to generate code from a data source. If you have any suggestions, please feel free to contribute by opening an issue or a pull-request.

If you need to generate code from a data-source, but Stencil CLI doesn’t fit your needs, check out GYB.

Thanks for reading!