测试已测试 | ✓ |
语言语言 | SwiftSwift |
授权 | MIT |
发布最后发布 | 2017年4月 |
SwiftSwift 版本 | 3.0 |
SPM支持 SPM | ✓ |
由 Aleksander Zubala,Łukasz Przytuła 维护。
欢迎来到轻量级且简洁的依赖注入框架 Swifjection。
这个项目的主要思想是为 Swift 对象实现 DI,而这些对象不继承自 Objective-C 类。
我们最初是为自己的用途构建 Swifjection 的。以下是我们动机的关键点:
NSObject
Swift
类型,只需遵守简单的 Injectable
协议NSObject
子类或遵守 Injectable
协议的类无需执行任何操作Swifjection 通过 CocoaPods 提供。要安装它,只需将以下行添加到您的 Podfile 中
pod "Swifjection"
Swifjection 包含一个符合 Injecting
协议的 Swifjector
类,这是您应用程序的默认注入器。
(目前)我们的框架不提供存储默认注入器的功能,我们建议您创建一个并在 AppDelegate
中使用我们的 SwifjectorFactory
存储它——这是一个辅助类,它可以检测应用程序是否正在运行 specs,并允许您设置 spec 注入器(请参阅 Tests 设置)
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var injector: Swifjector?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let binding = Binding()
[...]
injector = SwifjectorFactory(bindings: bindings).injector
[...]
return true
}
}
与任何其他 DI 框架一样,您可以设置要注入的对象的映射。目前 Swifjection 支持
闭包到类型的映射
let bindings = Bindings()
bindings.bind(type: ClassConformingToProtocol.self) { injector in
let object = MyClass() // create your object
object.setup() // do some additional setup
return object
}
实例到类型的映射
let bindings = Bindings()
let object = MyClass()
bindings.bind(object: object, toType: MyClass.self) // binding object to class
bindings.bind(object: object, toType: MyProtocol.self) // binding object to protocol
let structObject = MyStruct()
bindings.bind(object: structObject, toType: MyProtocol.self) // binding struct to protocol
类型到类型的映射
let bindings = Bindings()
bindings.bind(type: MyClass.self, toType: MyProtocol.self) // binding class to protocol
单例绑定
let bindings = Bindings()
bindings.bindSingleton(forType: MyClass.self) // binding class as singleton
使用 Swifjection 作为依赖注入框架的最大优势是您不需要在模块中将所有类型都映射。当模块中没有实例或闭包映射到类型时,Swifjection 会对以下条件之一满足时尝试创建一个全新实例
NSObject
- 实例是通过调用 init
创建的Injectable
协议 - 实例是通过调用 init?(injector:)
创建的否则注入器将返回 nil。
Swifjector
创建对象Injecting
协议为 getObject(withType:)
泛型函数提供了默认实现,该函数返回传递为参数的 T
类型的对象。
let object = injector.getObject(withType: MyClass.self)
let otherObject: MyClass? = injector.getObject(withType: MyClass.self) // This does not require any casting
除了这个函数之外,我们还实现了 subscript
函数,该函数接受类型为参数,但不是泛型,因此在某些情况下可能需要显式转换返回的对象。
let object = injector[MyClass.self]
let otherObject: MyClass? = injector[MyClass.self] as MyClass // This requires explicit casting
可以注入并且/或有依赖关系要注入的每个类或结构体都应该遵守 Injectable
协议
protocol Injectable {
init?(injector: Injecting)
func injectDependencies(injector: Injecting)
}
这样,当您的类或结构体是通过注入器(使用 init?(injector:)
)创建的,稍后它会收到 injectDependencies(injector:)
回调。这是您从 injector
中获取依赖的地方。
例如,假设您有两个类
class Foo {
}
class Bar {
var foo: Foo?
}
为了使用 Swifjection
注入 foo
属性,您需要更改以下代码
class Foo: Injectable {
required convenience init(injector: Injecting) {
self.init()
}
}
class Bar: Injectable {
var foo: Foo?
required convenience init(injector: Injecting) {
self.init()
}
func injectDependencies(injector: Injecting) {
foo = injector.getObject(withType: Foo.self)
}
}
SwifjectorFactory
辅助类检测应用程序是否正在运行规格说明,如果是这样,则不会创建 Swifjector 实例。这在 AppDelegate 中特别有用,在那里注入器已设置,并向其他对象传播。App delegate 在单元测试中初始化,并创建和使用了常规注入器。当 SwifjectorFactory
检测到正在运行单元测试时,它不会创建注入器。
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var injector: Swifjector?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let binding = Binding()
[...]
injector = SwifjectorFactory(bindings: bindings).injector // This will return nil during unit testing
[...]
return true
}
}
您可以使用 Swifjection 绑定在单元测试期间用模拟/虚构对象替换真实对象。为了更详细地说明这一点,让我们使用上述示例中的两个类
class Foo: Injectable {
required convenience init(injector: Injecting) {
self.init()
}
}
class Bar: Injectable {
var foo: Foo?
required convenience init(injector: Injecting) {
self.init()
}
func injectDependencies(injector: Injecting) {
foo = injector.getObject(withType: Foo.self)
}
}
如果我们要测试 Bar
类,我们希望使用模拟/虚构的 Foo
,而不是真实实例,以确保它返回我们期望它返回的值。让我们添加一个虚构的 Foo
类
class FakeFoo: Foo {
// override any functions needed for testing
}
现在我们可以在测试中使用它
let specBindings = Bindings()
let foo = FakeFoo()
specBindings.(object: foo, toType: Foo.self)
let injector = Swifjector(bindings: specBindings)
let barSUT = injector.getObject(withType: Bar.self)
// do the testing
Swifjection 适用于 MIT 许可证。请参阅 LICENSE 文件以获取更多信息。