Swinject
Swinject 是一个用于 Swift 的轻量级 依赖注入 框架。
依赖注入(DI)是一种软件设计模式,它通过控制反转(IoC)来解决依赖问题。在该模式中,Swinject 帮助您将应用程序拆分为松散耦合的组件,这使得开发、测试和维护变得更加容易。Swinject 通过 Swift 的泛型和一等函数来定义应用程序的依赖关系,使其简单且流畅。
功能
- 纯 Swift 类型支持
- 带有参数的注入
- 构造函数/属性/方法注入
- 初始化回调
- 循环依赖注入
- 对象作用域为 None(短暂)、图、容器(单例)和层次结构
- 支持引用类型和 值类型
- 自注册(自身绑定)
- 容器层次结构
- 线程安全
- 模块化组件
扩展
- SwinjectPropertyLoader:从资源中加载属性值。
- SwinjectStoryboard:通过 Storyboard 自动进行依赖注入。
- Swinject-CodeGen: 从CSV/YAML文件定义的依赖中生成
Container
的 类型安全代码。 - SwinjectAutoregistration: 利用Swift泛型实现服务的自动注册。
需求
- iOS 9.0+ / macOS 10.10+ / watchOS 2.0+ / tvOS 9.0+
- Xcode 10.2+
- Swift 4.2+
- Carthage 0.18+(如果您使用的话)
- CocoaPods 1.1.1+(如果您使用的话)
安装
Swinject可以通过Carthage、CocoaPods或Swift包管理器获取。
Carthage
要使用Carthage安装Swinject,请将以下行添加到您的Cartfile
中。
github "Swinject/Swinject"
# Uncomment if you use SwinjectStoryboard
# github "Swinject/SwinjectStoryboard"
然后运行carthage update --no-use-binaries
命令,或者直接运行carthage update
。有关Carthage的安装和使用详情,请访问其项目页面。
CocoaPods
要使用CocoaPods安装Swinject,请将以下行添加到您的Podfile
中。
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '9.0' # or platform :osx, '10.10' if your target is OS X.
use_frameworks!
pod 'Swinject'
# Uncomment if you use SwinjectStoryboard
# pod 'SwinjectStoryboard'
然后运行pod install
命令。有关CocoaPods的安装和使用详情,请访问其官方网站。
Swift包管理器
在Package.swift
中添加以下内容
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
.package(url: "https://github.com/Swinject/Swinject.git", from: "2.8.0")
],
targets: [
.target(
name: "MyProject",
dependencies: [..., "Swinject"]
)
...
]
文档
基本用法
首先将一个服务和组件对注册到 Container
中,其中组件由注册的闭包创建。在这个例子中,Cat
和 PetOwner
分别是实现 Animal
和 Person
服务协议的组件类。
let container = Container()
container.register(Animal.self) { _ in Cat(name: "Mimi") }
container.register(Person.self) { r in
PetOwner(pet: r.resolve(Animal.self)!)
}
然后从容器中获取一个服务实例。将人解析为宠物主人,与叫做咪咪的猫玩耍!
let person = container.resolve(Person.self)!
person.play() // prints "I'm playing with Mimi."
协议和类的定义如下
protocol Animal {
var name: String? { get }
}
class Cat: Animal {
let name: String?
init(name: String?) {
self.name = name
}
}
和
protocol Person {
func play()
}
class PetOwner: Person {
let pet: Animal
init(pet: Animal) {
self.pet = pet
}
func play() {
let name = pet.name ?? "someone"
print("I'm playing with \(name).")
}
}
注意,当将 Person
解析为 PetOwner
的实例时,PetOwner
的 pet
会自动设置为 Cat
的实例。如果提供了一个已经设置好的容器,你就不必关心服务的实际类型以及如何带有它们的依赖来创建。
服务注册位置
在使用之前,必须将服务注册到容器中。典型的注册方法将取决于你是否使用 SwinjectStoryboard
。
以下视图控制器类在下面的例子中除了上面的协议和类外还会使用。
class PersonViewController: UIViewController {
var person: Person?
}
使用 SwinjectStoryboard
在Swift源文件顶部导入 SninjectStoryboard。
import SwinjectStoryboard
如果你使用 SwinjectStoryboard,应该在 SwinjectStoryboard
的扩展中将服务注册。有关详细说明,请参阅 SwinjectStoryboard 的项目页面。
extension SwinjectStoryboard {
@objc class func setup() {
defaultContainer.register(Animal.self) { _ in Cat(name: "Mimi") }
defaultContainer.register(Person.self) { r in
PetOwner(pet: r.resolve(Animal.self)!)
}
defaultContainer.register(PersonViewController.self) { r in
let controller = PersonViewController()
controller.person = r.resolve(Person.self)
return controller
}
}
}
不使用SwinjectStoryboard
如果您没有使用SwinjectStoryboard
来实例化视图控制器,则应在应用程序的AppDelegate
中将服务注册到容器中。在application:didFinishLaunchingWithOptions:
退出之前注册将确保在它们被使用之前适当设置服务。
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
let container: Container = {
let container = Container()
container.register(Animal.self) { _ in Cat(name: "Mimi") }
container.register(Person.self) { r in
PetOwner(pet: r.resolve(Animal.self)!)
}
container.register(PersonViewController.self) { r in
let controller = PersonViewController()
controller.person = r.resolve(Person.self)
return controller
}
return container
}()
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
// Instantiate a window.
let window = UIWindow(frame: UIScreen.main.bounds)
window.makeKeyAndVisible()
self.window = window
// Instantiate the root view controller with dependencies injected by the container.
window.rootViewController = container.resolve(PersonViewController.self)
return true
}
}
注意,示例使用了一个便利初始化器,它接受一个闭包将该服务注册到Container
的新实例。
在Playground中播放!
该项目包含Sample-iOS.playground
以展示Swinject的功能。下载或克隆项目,运行playground,对其进行修改,并玩起来,以了解Swinject。
要在项目中共跑playground,首先构建项目,然后在Xcode中选中编辑 > 执行Playground
菜单。
示例应用
可以在GitHub上找到一些使用Swinject的示例应用。
博客文章
以下博客文章介绍了依赖注入和Swinject的概念。
- iOS依赖注入教程:开始阶段 by Irina Galata
- Swinject iOS教程:开始阶段 by Gemma Barlow
- 使用Swinject进行iOS依赖注入 by Ali Akhtar
- iOS依赖注入:完整指南 by Vitaly Batrakov
感谢作者们的贡献!
贡献指南
关于提交问题、提问或者开启pull请求的指南,请参见这里。
有问题?
- Stack Overflow我们正在尝试监控标签为
swinject
的问题。
致谢
Swinject的依赖注入容器特性受到以下项目的启发
- Ninject - Enkari, Ltd 和 Ninject项目贡献者.
- Autofac - Autofac项目。
以及高度受到以下项目的启发
许可证
MIT许可证。有关详情,请参阅LICENSE文件。