使用 Swift Macros 进行无成本依赖注入
MDI
利用 Swift 宏实现零成本依赖注入
功能
- 通过 Swift 宏实现零成本依赖注入
@AutoRegister
对所有工厂的依赖关系调用resolve()
@SingletonRegister
遵循与@AutoRegister
相同的原则,但将生成的值存储(并公开)在单例中@FactoryRegister
向resolve(...)
方法公开所有参数
- 类型安全
- 生成纯 Swift
要求
- 需要 Swift 5.9
使用示例
定义一种类型,它既充当“程序集中心”,又充当解析入口点。
一个空 enum
应该足够了。
enum Dependency { }
在本示例中,程序集将在 Dependency
的扩展中定义(虽然这不是强制的,并且可以在类型声明上直接完成)。
在下面的示例中,我们将假定以下类型:
protocol ABTestingProtocol { }
protocol CodeGuardsProtocol { }
protocol ThemeProtocol { }
final class ABTesting: ABTestingProtocol { }
final class CodeGuards: CodeGuardsProtocol { }
final class Theme: ThemeProtocol {
private let abTesting: ABTestingProtocol
private let codeGuards: CodeGuardsProtocol
init(
abTesting: ABTestingProtocol,
codeGuards: CodeGuardsProtocol
) {
self.abTesting = abTesting
self.codeGuards = codeGuards
}
}
然后创建一个程序集来注册它们:
import MDI
@SingletonRegister((any ABTestingProtocol).self, factory: ABTesting.init)
@SingletonRegister((any CodeGuardsProtocol).self, factory: CodeGuards.init)
@SingletonRegister((any ThemeProtocol).self, factory: Theme.init(abTesting:codeGuards:))
extension Dependency { }
@SingletonRegister
将对 ABTestingProtocol
和 CodeGuardsProtocol
调用 resolve
。
由于两者都在程序集中声明(注意它们很容易在其他地方声明),因此这会成功;否则,我们将收到编译器错误。
请注意,在前面的示例中,所有依赖关系都是单例,但显然不必如此。
如果使用了 @AutoRegister
:
import MDI
@AutoRegister((any ABTestingProtocol).self, factory: ABTesting.init)
@AutoRegister((any CodeGuardsProtocol).self, factory: CodeGuards.init)
@AutoRegister((any ThemeProtocol).self, factory: Theme.init(abTesting:codeGuards:))
extension Dependency { }
在每次调用 resolve(...)
时都会创建注册类型的新的实例。
最后,某些依赖关系需要无法解析的参数,而是在实例化时传递。
这可以通过 @FactoryRegister
轻松实现。
在下面的示例中,我们可以解析 ThemeProtocol
,但不一定是 boot: Date
或 sessionId: String
。
protocol AppContextProtocol {}
final class AppContext: AppContextProtocol {
let boot: Date
let sessionId: String
let theme: ThemeProtocol
init(
boot: Date,
sessionId: String,
theme: ThemeProtocol
) {
self.boot = boot
self.sessionId = sessionId
self.theme = theme
}
}
通过使用 @FactoryRegister
,我们可以在创建工厂方法时公开所需的参数,甚至利用 resolve(...)
来解析 ThemeProtocol
:
import MDI
private extension AppContext {
static func factory(
boot: Date,
sessionId: String
) -> AppContext {
return AppContext(
boot: boot,
sessionId: sessionId,
theme: Dependency.resolve()
)
}
}
@FactoryRegister(
(any AppContextProtocol).self,
parameterTypes: Date.self, String.self,
factory: AppContext.factory(boot:sessionId:)
)
extension Dependency { }
使用此方法,Dependency
将公开类型为以下的 resolve
方法:
Dependency.resolve(_ arg1: Date, _ arg1: String) -> any AppContextProtocol
安装
Swift 包管理器
你可以通过将 Swift 包管理器添加到你的 Package.swift
文件作为依赖项来使用它来安装你的软件包: