Code Generation in Swift
November 6, 2019
#swift
#workflow
#optimization
While working on iOS project you can find yourself quite frequently writing boilerplate code. This is the code that you prefer not to write by hand and tempt to copy & paste most of the times. It can lead to certain mistakes and even crashes in runtime. In this situation, code generation might be useful. It lets you generate boring and repetitive code and make an application more reliable.
In this post let’s take a look at the use-cases when code generation makes sense and make an overview of the tools we can use to automate it.
Code generation use cases
Code generation is not a new technique and is used already in many Android, Golang, etc. projects. You might be using Sourcery in your project already, that is a popular tool for code generation and metaprogramming in Swift.
First of all, let’s define the most common use cases for code generation:
- Resource accessors
- Test mocks
- Dependency Injection
- DTO models
It should be mentioned that Swift evolves, we don’t need code generation for certain tasks anymore. This includes automatic synthesis for Codable
, Equatable
, Hashable
and CaseIterable
protocols.
Resource accessors
Unfortunately there is no an equivalent to Android’s R class in iOS. That’s why access to application resources like assets, localization strings, etc. is evaluated in runtime. This can lead to certain issues when a resource is missing in the application bundle.
Luckily there is a great tool Swiftgen, which allows you to auto-generate Swift code for resources of your projects. With SwiftGen you get type-safe access to the resources and avoid the risk of using non-existing ones. It can generate constants for the next resource types:
- Asset Catalogs
- Localization strings
- IB Storyboards
- Colors
- Fonts
- JSON/YAML/Plist
- Core data models
SwiftGen uses Stencil templating language to define templates. It comes bundled with some ready-to-use templates and allows to create custom ones. I’d suggest checking the following guide to get an overview of Stencil and become familiar with its syntax.
Test mocks
In static languages like Swift unit testing with mocks is a bit problematic. The reason is that you can’t dynamically create or modify a class behavior in runtime. As an option, you can use OCMock
library, but it is limited to NSObject
subclasses and doesn’t provide full Swift support. We could use reflection, but it is quite restrained in Swift and is not suitable for creating mocks in runtime. The other options would be creating these test mocks manually by defining a class that conforms to a protocol. But it can result in a significant amount of tedious and repetitive work.
Luckily enough you can use tools to autogenerate mock objects that can be used in your tests:
With the help of those, you can automate writing and maintaining mock implementations, that you’ve done manually.
Dependency Injection
Dependency Injection is one of the fundamental software development patterns. It represents Inversion of Control technique for supplying dependencies to a class via constructor, public property or setter function.
As your project gets bigger, injecting dependencies and passing them down through several layers of abstractions becomes more time-consuming. This is the reason why you can start using DI framework in your project. Tools like Weaver and Needle are inspired by Dagger and utilize code generation. They take responsibility for generating the necessary boilerplate code to inject dependencies. Eventually you get more confidence by ensuring dependency injection code is compile-time safe: if it compiles, it works.
DTO models
Quite often when integrating with RESTful APIs you have to define lots of Data transfer objects. It is clear that this approach is inefficient and can lead to bugs in your code.
Luckily you can generate a client-side code if backend uses Swagger and exposes Open API Specification(Swagger Specification) files. There are several options for doing it:
- swagger online editor
- swagger-codegen official application
- SwagGen alternative tool, that has some improvements compared to the official one
It should be pointed out that both tools use templates for code generation. You can provide a custom one if you are not satisfied with the default syntax.
Other
Apple engineers are using code generation as well. Swift maintainers created a python-based templating tool called GYB, that is an acronym for “Generate Your Boilerplate”. It might be helpful when you have abstractions with a common structure. Instead of maintaining several versions of similar code, you can define a single template and generate the rest of classes/structs/enums. You can check Codable.swift.gyb template file to get an idea about GYB templates. There is also a nice article about GYB and how to adopt it in your project posted at NSHipster.
Conclusion
Code generation is an effective way to increase your productivity and make your application more robust. You might be familiar with code generation in iOS and tried Sourcery
or Swiftgen
already. If not, I’d suggest starting with Resource accessors or Test mocks code generation. In this way, you can remove the need to maintain the boilerplate code manually and focus on other important things.
Feel free to follow me on Twitter and ask questions related to this post.
Thanks for reading!