EasyInject
EasyInject 被设计为易于使用、轻量级的组合和依赖注入库。您不仅为特定类型注入实例,还可以为关键字提供实例,同时不丢失任何类型信息。这使得其 Injector
能够作为一个可组合的、动态的、类型安全的数据结构。它可以与一个包含多个类型的字典相媲美,而不会丢失类型安全性。
查看生成的文档,请访问 vknabel.github.io/EasyInject。
自版本 1.2.0 以来,EasyInject 支持 Swift 3 和 Swift 4。在 Swift 4 中,值只能通过下标访问,如果您仍在使用 Swift 3,请继续使用 Injector.resolving(for:)
。
安装
EasyInject 是一个 Swift 专用项目,支持 Swift 包管理器、Carthage 和 CocoaPods。
Swift 包管理器
import PackageDescription
let package = Package(
name: "YourPackage",
dependencies: [
.Package(url: "https://github.com/vknabel/EasyInject.git", majorVersion: 1)
]
)
Carthage
github "vknabel/EasyInject" ~> 1.2
CocoaPods
source 'https://github.com/CocoaPods/Specs.git'
use_frameworks!
pod 'EasyInject', '~> 1.2'
简介
为了注入依赖项,您首先需要通过实现Hashable
来准备您的密钥。
import EasyInject
enum ServicesKeyType { } // will never be instantiated
typealias Services = GenericProvidableKey<Services>
现在我们需要定义我们的键,通过设置带有String
和我们的类型提示的Provider
。
extension Provider {
static var baseUrl: Provider<Services, String> {
return Provider<Services, String>(for: "baseUrl")
}
static var networkService: Provider<Services, NetworkService> {
// produces a key of `networkService(...) -> Network`.
return .derive()
}
static var dataManager: Provider<Services, DataManager> {
return .derive()
}
}
final class NetworkService {
let baseUrl: String
init<I: Injector where I.Key == Services>(injector: inout I) throws {
print("Start: NetworkService")
baseUrl = try injector[.baseUrl] // or resolving(from: .baseUrl) in Swift 3.x
print("Finish: NetworkService")
}
}
final class DataManager {
let networkService: NetworkService
init<I: Injector where I.Key == Services>(injector: inout I) throws {
print("Start: DataManager")
networkService = try injector[.networkService]
print("Finish: DataManager")
}
}
LazyInjector
有一些Injector
可以选择,比如StrictInjector
或LazyInjector
。我们先选一个懒的(Lazy)的,并为我们的键提供一些值。
var lazyInjector = LazyInjector<Services>() // Only Services keys will fit in here
lazyInjector.provide(for: .baseUrl, usingFactory: { _ in
print("Return: BasUrl")
return "https://my.base.url/"
})
lazyInjector.provide(for: .dataManager, usingFactory: DataManager.init)
lazyInjector.provide(for: .networkService, usingFactory: NetworkService.init)
由于我们使用的是LazyInjector
,我们传递的任何闭包尚未被执行。它们将在它们被解析时执行。
// this will execute all factories we passed for our providers
do {
try lazyInjector.resolve(from: .dataManager)
} catch {
print("Error: \(error)")
}
因为我们选择了LazyInjector
,所以所有依赖项将在需要时自动解析。因此,生成的输出将是
Start: DataManager
Start: NetworkService
Return: BasUrl
Finish: NetworkService
Finish: DataManager
因此,由于我们的LazyInjector
的惰性,所有依赖项都将自动解析。循环依赖在解析时引发错误,以防止无限递归。
StrictInjector
如果使用StrictInjector
,前面的示例将失败,因为我们先提供了.dataManager
,然后再提供.networkService
,但是DataManager
需要一个.networkService
。
var strictInjector = StrictInjector<Services>()
strictInjector.provide(for: .baseUrl, usingFactory: { _ in
print("Return: BaseUrl")
return "https://my.base.url/"
})
strictInjector.provide(for: .dataManager, usingFactory: DataManager.init) // <-- missing .networkService
strictInjector.provide(for: .networkService, usingFactory: NetworkService.init)
do {
try strictInjector.resolve(from: .dataManager)
} catch {
print("Error: \(error)")
}
输出将是
Return: BaseUrl
Start: DataManager
Start: NetworkService
Finish: NetworkService
Error: keyNotProvided("networkService(...) -> NetworkService")
这种行为在调试您的LazyInjector
时可能很有用,以便检测依赖项循环。
您只需翻转.networkService
和.dataManager
的行,就可以修复此错误,并将导致以下输出
Return: BaseUrl
Start: NetworkService
Finish: NetworkService
Start: DataManager
Finish: DataManager
strictInjector = StrictInjector<Services>()
strictInjector.provide(for: .baseUrl, usingFactory: { _ in
print("Return: BaseUrl")
return "https://my.base.url/"
})
strictInjector.provide(for: .networkService, usingFactory: NetworkService.init)
strictInjector.provide(for: .dataManager, usingFactory: DataManager.init)
do {
try strictInjector.resolve(from: .dataManager)
} catch {
print("Error: \(error)")
}
GlobalInjector
一个 GobalInjector
包装另一个 Injector
使得它表现得像一个类。
let globalInjector = GlobalInjector(injector: strictInjector)
let second = globalInjector
// `globalInjector` may be mutated as it is a class.
second.provide("https://vknabel.github.io/EasyInject", for: .baseUrl)
if let left = try? globalInjector.resolve(from: .baseUrl),
let right = try? globalInjector.resolve(from: .baseUrl),
left == right {
// both `right` and `left` contain `"https://vknabel.github.io/EasyInject"` for `.baseUrl` due to reference semantics
}
组合注入器
一个 组合注入器
由两个其他 注入器
组成。调用 .resolve(from:)
将针对
通常左侧的 注入器
将是本地的,而右侧的一个是全球的。这使得能够将 组合注入器
从根控制器级联到叶子控制器。
var composedInjector = ComposedInjector(left: StrictInjector(), right: globalInjector)
composedInjector.provideLeft("https://vknabel.github.io/EasyInject/Structs/ComposedInjector.html", for: .baseUrl)
do {
try composedInjector.resolveBoth(from: .baseUrl)
// returns `("https://vknabel.github.io/EasyInject/Structs/ComposedInjector.html", "https://vknabel.github.io/EasyInject")`
} catch {
print("Error: \(error)")
}
作者
Valentin Knabel, [email protected]
许可协议
EasyInject 在 MIT 协议 下可用。