Swifjection 0.7.0

Swifjection 0.7.0

测试已测试
语言语言 SwiftSwift
授权 MIT
发布最后发布2017年4月
SwiftSwift 版本3.0
SPM支持 SPM

Aleksander ZubalaŁukasz Przytuła 维护。



  • Łukasz Przytuła 和 Aleksander Zubala

img

Build Status

关于

欢迎来到轻量级且简洁的依赖注入框架 Swifjection

这个项目的主要思想是为 Swift 对象实现 DI,而这些对象不继承自 Objective-C 类。

为什么使用 Swifjection?

我们最初是为自己的用途构建 Swifjection 的。以下是我们动机的关键点:

  • 注入依赖品不需要继承自 NSObject
  • 要注入 Swift 类型,只需遵守简单的 Injectable 协议
  • 注入 NSObject 子类或遵守 Injectable 协议的类无需执行任何操作
  • 清晰的简单绑定系统,受 Objective-C 框架 Objection 的启发
  • 轻量级——我们想要避免不必要的混乱,并尽可能地简化 API

安装

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 文件以获取更多信息。