1. Code Generation in Swift

Discover use-cases of code generation and get an overview of the tools you can use to automate it

在开发iOS项目时,你会发现自己经常编写样板代码。这是您不喜欢手动编写的代码,并且在大多数情况下尝试复制和粘贴。它可能会导致某些错误,甚至在运行时崩溃。在这种情况下,代码生成可能会有用。它允许您生成乏味和重复的代码,并使应用程序更加可靠。

在这篇文章中,让我们来看看代码生成有意义的用例,并概述一下我们可以用来自动化代码生成的工具。

1.1. Code generation use cases

代码生成不是一项新技术,已经在许多Android、Golang等项目中使用。你可能已经在你的项目中使用了Sourcery,这是Swift中一个用于代码生成和元编程的流行工具。

首先,让我们定义最常见的代码生成用例:

  • Resource accessors
  • Test mocks
  • Dependency Injection
  • DTO models

应该提到的是,随着Swift的发展,我们不再需要为某些任务生成代码。这包括可编码协议、可等量协议、可哈希协议和CaseIterable协议的自动合成。

1.2. Resource accessors

不幸的是,在iOS中没有类似于Android的R类。这就是为什么对应用资源(如资产、本地化字符串等)的访问需要在运行时进行评估。当应用程序包中缺少资源时,这可能会导致某些问题。

幸运的是,有一个很棒的工具Swiftgen,它允许你为你的项目资源自动生成Swift代码。 使用SwiftGen,您可以获得对资源的类型安全访问,并避免使用不存在的资源的风险。它可以为下一个资源类型生成常量:

  • Asset Catalogs
  • Localization strings
  • IB Storyboards
  • Colors
  • Fonts
  • JSON/YAML/Plist
  • Core data models

SwiftGen使用模板(Stencil)语言来定义模板。它与一些现成的模板捆绑在一起,并允许创建自定义模板。我建议检查下面的指南,以获得Stencil的概述,并熟悉它的语法。

1.3. Test mocks

在像Swift这样的静态语言中,带mock的单元测试有点问题。原因是不能在运行时动态创建或修改类行为。作为一种选择,你可以使用OCMock库,但它仅限于NSObject子类,并且不提供完整的Swift支持。

我们可以使用反射,但它在Swift中受到了很大限制,不适合在运行时创建模拟。 其他的选择是通过定义一个符合协议的类来手动创建这些测试模拟。

但它会导致大量乏味和重复的工作。

幸运的是,你可以使用工具自动生成可以在你的测试中使用的模拟对象:

在这些帮助下,您可以自动编写和维护手动完成的模拟实现。

1.4. Dependency Injection

依赖注入是一种基本的软件开发模式。它表示通过构造函数、公共属性或setter函数为类提供依赖关系的控制反转技术。

随着项目的规模越来越大,注入依赖项并将它们通过几个抽象层传递下去会变得更加耗时。这就是为什么您可以在项目中开始使用DI框架的原因。像WeaverNeedle这样的工具是受到Dagger的启发并利用代码生成的。他们负责生成必要的样板代码来注入依赖项。最终,通过确保依赖注入代码在编译时是安全的,您会获得更多的信心:如果它能编译,它就能工作。

1.5. DTO models

在与RESTful api集成时,通常需要定义大量的数据传输对象。很明显,这种方法效率低下,可能会导致代码中的错误。

幸运的是,如果后端使用Swagger并公开Open API Specification(Swagger Specification)文件,您可以生成客户端代码。这里有几个选择:

应该指出的是,这两种工具都使用模板来生成代码。如果不满意默认语法,可以提供自定义语法。

1.6. Other

苹果的工程师也在使用代码生成技术。Swift维护人员创建了一个名为GYB的基于python的模板工具,它是“生成你的样板(Generate Your Boilerplate)”的首字母缩写。当您拥有具有公共结构的抽象时,它可能会很有帮助。您可以定义一个模板并生成其余的类/结构/枚举,而不是维护类似代码的多个版本。

NSHipster上还有一篇关于GYB以及如何在项目中采用GYB的文章。

1.7. Conclusion

代码生成是提高生产力和使应用程序更健壮的有效方法。你可能熟悉iOS中的代码生成,并尝试过Sourcery或Swiftgen。 如果没有,我建议从资源访问器或测试模拟代码生成开始。通过这种方式,您可以消除手动维护样板代码的需要,并将重点放在其他重要的事情上。

Thanks for reading!