测试已测试 | ✗ |
语言语言 | Obj-CObjective C |
许可 | MIT |
发布上次发布 | 2017年2月 |
SwiftSwift 版本 | 3.0 |
维护者 Wolfgang Lutz。
Swinject-CodeGen 通过生成明确的使用 Swinject 进行注册和解 resolved 的函数,提供了一种避免类值和名称字符串重复使用的方法。通过这种方式,我们还可以生成在 resolved 时使用的类型化的元组,从而使代码具有更好的文档性和更少的错误。
对于 Cocoapods
$PODS_ROOT/Swinject-CodeGen/bin/swinject_codegen -i baseInput.csv -o extensions/baseContainerExtension.swift
对于 Carthage
$SRCROOT/Carthage/Checkouts/Swinject-CodeGen/bin/swinject_codegen -i baseInput.csv -o extensions/baseContainerExtension.swift
然后在每次构建运行时生成代码。
在使 用 Swinject 时,每当进行以下操作时,就会出现很多重复的定义:
container.register(PersonType.self, name: "initializer") { r in
InjectablePerson(pet: r.resolve(AnimalType.self)!)
}
let initializerInjection = container.resolve(PersonType.self, name:"initializer")!
元组 (PersonType.self, name:"initializer") 在代码中变得非常冗余。
此外,在调用时使用参数,如以下示例所示:
container.register(AnimalType.self) { _, name in Horse(name: name) }
let horse1 = container.resolve(AnimalType.self, argument: "Spirit") as! Horse
`argument: "Spirit"` 部分在调用时不具有严格的类型。
我们通过使用代码生成来解决这两个问题。
输入可以是 .csv 或者 .yml 格式
可以使用以下调用将 example.csv 转换为 example.csv.yml (也适用于 .yml):
./swinject_codegen -i example.csv -c
...
我们定义的基本 CSV 结构如下:
SourceClassName; TargetClassName; Identifier; Argument 1 ... 9
上述示例将转换为:
PersonType; InjectablePerson; initializer
以生成 `registerPersonType_initializer` 和 `resolvePersonType_initializer` 函数。
请参考下面的示例进行更多示例。
我们决定使用 `;` 作为分隔符而不是 `,`,以便允许使用元组作为类型。
Ruby 解析器允许使用 `//` 和 `#` 作为注释。空行将被忽略,可用于分组。
可以使用 `#= <header>` 来指定附加行,例如:`#= import KeychainAccess`
当使用类型化的字典或数组作为参数时,请使用 Array<Type>
替代 [Type]
,并使用 Dictionary<TypeA, TypeB>
替代 [TypeA:TypeB]
PersonType; InjectablePerson; initializer; additionalNames:Array<String>; family:Dictionary<String, String>;
以下为 .yml 定义示例
---
HEADERS:
- import ADependency
DEFINITIONS:
- service: PersonType
component: InjectablePerson
name: initializer
- service: PersonType
component: InjectablePerson
- service: PersonType
component: PersonType
- service: AnotherPersonType
component: AnotherPersonType
- service: PersonType
component: InjectablePerson
arguments:
- argument_name: argument_name
argument_type: argument_type
- service: PersonType
component: InjectablePerson
arguments:
- argument_name: argument_name
argument_type: argument_type
- argument_name: argument_typewithoutspecificname
argument_type: argument_typeWithoutSpecificName
- argument_name: title
argument_type: String
- argument_name: string
argument_type: String
- service: PersonType
component: InjectablePerson
name: initializer
arguments:
- argument_name: argument_name
argument_type: argument_type
- argument_name: argument_typewithoutspecificname
argument_type: argument_typeWithoutSpecificName
- argument_name: title
argument_type: String
- argument_name: string
argument_type: String
PersonType
// this code is autogenerated, do not modify!
import Swinject
extension Resolver {
func resolvePersonType() -> PersonType {
return self.resolve(PersonType.self)!
}
}
extension Container {
@discardableResult func registerPersonType(registerClosure: @escaping (_ resolver: Resolver) -> (PersonType)) -> ServiceEntry<PersonType> {
return self.register(PersonType.self, factory: registerClosure)
}
}
PersonType; InjectablePerson
// this code is autogenerated, do not modify!
import Swinject
extension Resolver {
func resolveInjectablePerson() -> InjectablePerson {
return self.resolve(PersonType.self) as! InjectablePerson
}
}
extension Container {
@discardableResult func registerInjectablePerson(registerClosure: @escaping (_ resolver: Resolver) -> (InjectablePerson)) -> ServiceEntry<PersonType> {
return self.register(PersonType.self, factory: registerClosure)
}
}
PersonType; InjectablePerson; initializer
// this code is autogenerated, do not modify!
import Swinject
extension Resolver {
func resolveInjectablePerson_initializer() -> InjectablePerson {
return self.resolve(PersonType.self, name: "initializer") as! InjectablePerson
}
}
extension Container {
@discardableResult func registerInjectablePerson_initializer(registerClosure: @escaping (_ resolver: Resolver) -> (InjectablePerson)) -> ServiceEntry<PersonType> {
return self.register(PersonType.self, name: "initializer", factory: registerClosure)
}
}
PersonType; InjectablePerson; ; argumentName:ArgumentType
// this code is autogenerated, do not modify!
import Swinject
extension Resolver {
func resolveInjectablePerson(argumentName: ArgumentType) -> InjectablePerson {
return self.resolve(PersonType.self, argument: argumentName) as! InjectablePerson
}
}
extension Container {
@discardableResult func registerInjectablePerson(registerClosure: @escaping (_ resolver: Resolver, _ argumentName: ArgumentType) -> (InjectablePerson)) -> ServiceEntry<PersonType> {
return self.register(PersonType.self, factory: registerClosure)
}
}
如果没有给出显式名称,则使用小写类型作为参数名称。
PersonType; InjectablePerson; ; argumentName:ArgumentType; ArgumentTypeWithoutSpecificName; title:String; String
// this code is autogenerated, do not modify!
import Swinject
extension Resolver {
func resolveInjectablePerson(argumentName: ArgumentType, argumenttypewithoutspecificname: ArgumentTypeWithoutSpecificName, title: String, string: String) -> InjectablePerson {
return self.resolve(PersonType.self, arguments: argumentName, argumenttypewithoutspecificname, title, string) as! InjectablePerson
}
}
extension Container {
@discardableResult func registerInjectablePerson(registerClosure: @escaping (_ resolver: Resolver, _ argumentName: ArgumentType, _ argumenttypewithoutspecificname: ArgumentTypeWithoutSpecificName, _ title: String, _ string: String) -> (InjectablePerson)) -> ServiceEntry<PersonType> {
return self.register(PersonType.self, factory: registerClosure)
}
}
PersonType; InjectablePerson; initializer; argumentName:ArgumentType; ArgumentTypeWithoutSpecificName; title:String; String
// this code is autogenerated, do not modify!
import Swinject
extension Resolver {
func resolveInjectablePerson_initializer(argumentName: ArgumentType, argumenttypewithoutspecificname: ArgumentTypeWithoutSpecificName, title: String, string: String) -> InjectablePerson {
return self.resolve(PersonType.self, name: "initializer", arguments: argumentName, argumenttypewithoutspecificname, title, string) as! InjectablePerson
}
}
extension Container {
@discardableResult func registerInjectablePerson_initializer(registerClosure: @escaping (_ resolver: Resolver, _ argumentName: ArgumentType, _ argumenttypewithoutspecificname: ArgumentTypeWithoutSpecificName, _ title: String, _ string: String) -> (InjectablePerson)) -> ServiceEntry<PersonType> {
return self.register(PersonType.self, name: "initializer", factory: registerClosure)
}
}
使用开头提供的示例,我们可以现在代替
container.register(PersonType.self, name: "initializer") { r in
InjectablePerson(pet: r.resolve(AnimalType.self)!)
}
let initializerInjection = container.resolve(PersonType.self, name:"initializer")!
写
container.registerPersonType_initializer { r in
InjectablePerson(pet: r.resolve(AnimalType.self)!)
}
let initializerInjection = container.resolvePersonType_initializer()
同样
container.register(AnimalType.self) { _, name in Horse(name: name) }
let horse1 = container.resolve(AnimalType.self, argument: "Spirit")
变为
container.registerAnimalType { (_, name:String) in
Horse(name: name)
}
let horse1 = container.resolveAnimalType("Spirit")
脚本还会生成 migration.sh 文件(使用 -m 开关时),这些文件使用 sed 遍历代码并替换 resolve 和 register 的简单情况(即没有参数)。还没有自动迁移支持具有参数的情况。只需从项目根目录调用 .sh 文件并在 git-GUI 中比较结果即可。
我们目前在tvOS和iOS上的两个中型应用中使用了代码生成。
我们发现,由于减少了重复内容和自动完成,我们的代码阅读和编写变得更加方便。我们还能够更好地了解通过依赖注入可用的类。更改一些定义会立即导致出现在错误信息中。我们能够使用当前实现替换所有出现的.resolve(
和.register(
。
将代码生成与Swinject结合的原始想法来自Daniel Dengler、David Kraus 和 Wolfgang Lutz。