SwinjectAutoregistration

Build Status Carthage compatible CocoaPods Version License Platform Swift Version

SwinjectAutoregistration 是 Swinject 的一个扩展,它允许您自动注册您的服务,并大大减少样板代码的数量。

要求

安装

Swinject 可以通过 CarthageCocoaPodsSwift Package Manager 安装。

Carthage

要使用 Carthage 安装 Swinject,请将以下行添加到您的 Cartfile

github "Swinject/Swinject" "2.9.1"
github "Swinject/SwinjectAutoregistration" "2.9.1"

然后运行 carthage update --use-xcframeworks --no-use-binaries 命令或者直接运行 carthage update --use-xcframeworks。有关 Carthage 的安装和使用的详细信息,请访问 其项目页面

CocoaPods

要使用 CocoaPods 安装 Swinject,请将以下行添加到您的 Podfile

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '11.0' # or platform :osx, '10.13' if your target is OS X.
use_frameworks!

pod 'Swinject', '2.9.1'
pod 'SwinjectAutoregistration', '2.9.1'

然后运行 pod install 命令。有关 CocoaPods 的安装和使用的详细信息,请访问 其官方网站

Swift Package Manager

Package.swift 中添加以下内容

dependencies: [
    .package(url: "https://github.com/Swinject/SwinjectAutoregistration.git", from: "2.9.1")
],
targets: [
    .target(
        name: "MyProject",
        dependencies: [..., "SwinjectAutoregistration"]
    )
    ...
]

注册

这是一个自动注册宠物主人的简单示例

let container = Container()
container.register(Animal.self) { _ in Cat(name: "Mimi") } // Regular register method
container.autoregister(Person.self, initializer: PetOwner.init) // Autoregistration

其中 PetOwner 看起来像这样

class PetOwner: Person {
    let pet: Animal

    init(pet: Animal) {
        self.pet = pet
    }
}

autoregister 函数接收到 PetOwner 的初始化器 init(pet:Animal)。 从其签名中,Swinject 知道它需要一个依赖项 Animal 并从容器中解析它。 不需要其他任何东西。

当用于注册具有许多依赖项的服务时,自动注册变得特别有用。 比较自动注册代码

container.autoregister(MyService.self, initializer: MyService.init)

与纯 Swinject 中的等效代码

container.register(MyService.self) { r in
    MyService(dependencyA: r.resolve(DependencyA.self)!, dependencyB: r.resolve(DependencyB.self)!, dependencyC: r.resolve(DependencyC.self)!, dependencyD: r.resolve(DependencyD.self)!)
}

另一个优点是,如果在开发过程中添加更多依赖项,则无需重写注册代码。

带名称的注册

服务也可以被赋予名称 - 与常规注册方法相同。

container.autoregister(Person.self, name: "johnny", initializer: PetOwner.init)

参数

您还可以将自动注册用于具有动态参数的服务。需要将名称作为参数传递的宠物主人定义如下

class PetOwner: Person {
    let name: String
    let pet: Animal

    init(name: String, pet: Animal) {
        self.name = name
        self.pet = pet
    }
}

并像这样注册

container.autoregister(Person.self, argument: String.self, initializer: PetOwner.init)

Swinject 将注册 Person 并传入 String 类型的参数。 当调用 container.resolve(Person.self, argument: "Michael") 时,Swinject 不会尝试将 String 解析为依赖项,而是将 "Michael" 作为名称传递。

要也将宠物作为参数传递,您可以调用

container.autoregister(Person.self, arguments: String.self, Animal.self, initializer: PetOwner.init)
//or
container.autoregister(Person.self, arguments: Animal.self, String.self, initializer: PetOwner.init)

所列参数的顺序可以互换。 自动注册不能与相同类型的更多参数和/或依赖项一起使用。

这是什么样的魔法?

想知道它是如何工作的吗? 泛型被大量用于自动注册。 对于注册具有两个依赖项的服务,使用类似于以下函数的函数

public func autoregister<Service, A, B>(_ service: Service.Type, initializer: (A, B) -> Service) -> ServiceEntry<Service> {
   return self.register(service.self, factory: { r in
       return initializer(r.resolve(A.self)!, r.resolve(B.self)!)
   } as (ResolverType) -> Service)
}

初始化器是一个像其他任何东西一样的函数。 通过将其作为参数传递,其依赖项可以被推断为 (A, B) 并自动解析。 这些函数是为最多 20 个依赖项生成的。 查看 代码 以获取更多信息。

运算符

此扩展还旨在减少样板代码的数量,同时提高注册代码的可读性。 因此,引入了运算符 ~>

Petowner(pet: r~>)

// equivalent to

Petowner(pet: r.resolve(Animal.self)!)

依赖关系再次从初始化器中的类型推断。 要指定一个具体的类,您可以使用

Petowner(pet: r ~> Cat.self)

要使用命名服务

Petowner(pet: r ~> (Cat.self, name: "mimi"))

或传递参数

Petowner(pet: r ~> (Cat.self, argument: "Mimi"))
Petowner(pet: r ~> (Cat.self, arguments: ("Mimi", UIColor.black)))

局限性

当一个服务有多个初始化器时,swift 编译器不能确定应该使用哪个初始化器,你会得到一个 ambigious use of init(x: y: z:)。如果该服务正在扩展另一个具有相同数量参数的初始化器的类,也会发生这种情况。

解决方案是像这样指定初始化器

container.autoregister(Person.self, initializer: PetOwner.init(name:pet:))

自动注册不能用于其初始化器中的命名依赖项。 无法从初始化器获取依赖项的名称。 例如,以下代码无法自动注册

container.register(Animal.self, name: "mimi") { _ in Cat(name: "Mimi") }
container.register(Animal.self, name: "charles") { _ in Cat(name: "Charles") }
container.register(Person.self) {
    PetOwner(pet: r.resolve(Animal.self, name: "mimi")
}

Swift 5.3

由于 Swift 5.3 在推断具有默认值的变量的结构中的初始化器时,编译器的行为有所不同

struct Cat {
    let height: Int = 50
}

编译器将生成两个 init 函数

Cat.initCat.init(height:)

由于 Swift 5.3 以下注册

container.autoregister(Animal.self, initializer: Cat.init)

将尝试使用 Cat.init(height:),然后会失败,并显示 Unresolved service: Int Initializer: (Int) -> Animal

解决方案是使编译器使用不带参数的 init

container.autoregister(Animal.self, initializer: Cat.init as () -> Cat)

对于维护者

制作新的发行版本

我们的发布程序描述为 Makefile。 运行 make help 命令获取更多信息。

鸣谢

SwinjectAutoregistration 泛型受以下启发

许可证

MIT 许可证。 有关详细信息,请参阅 LICENSE 文件