EasyInject 1.3.0

EasyInject 1.3.0

测试已测试
语言语言 SwiftSwift
许可证 MIT
发布上次发布2020年3月
SPM支持 SPM

Valentin Knabel 维护。



CocoaPods CocoaPods Install License

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 包管理器CarthageCocoaPods

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可以选择,比如StrictInjectorLazyInjector。我们先选一个懒的(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 协议 下可用。