EasyDiライブラリには、Swiftの依存関係コンテナが含まれています。 このライブラリの構文は、迅速な開発と効率的な使用のために特別に設計されています。 200行に収まりますが、成人のDiライブラリに必要なものはすべて知っています。
-オブジェクトの作成と既存のオブジェクトへの依存関係の注入
-コンテナへの分離-アセンブリ
-依存関係を解決するタイプ:オブジェクトのグラフ、シングルトン、プロトタイプ
-循環依存関係の解決
-オブジェクトの置換とテストの依存関係テスト
EasyDiには、登録/分離の解決はありません。 代わりに、依存関係は次のように説明されます。
var apiClient: IAPIClient {
return define(init: APIClient()) {
$0.baseURl = self.baseURL
}
}
→
Cocoapods / EasyDi→
Github / EasyDi« DI », :
DI ?( )
, 5 .
, DI :
- . UI, , . UI , UI.
- . , UI, .
- . , API, UI.
DI :
.
.. :
class OrderViewController {
func didClickShopButton(_ sender: UIButton?) {
APIClient.sharedInstance.purchase(...)
}
}
:
protocol IPurchaseService {
func perform(...)
}
class OrderViewController {
var purchaseService: IPurchaseService?
func didClickShopButton(_ sender: UIButton?) {
self.purchaseService?.perform(...)
}
}
SOLID
(objc.io #15 DI) (wikipedia. SOLID).
EasyDi ( )
: ViewController . . .
protocol IPurchaseService {
func perform(with objectId: String, then completion: (success: Bool)->Void)
}
class PurchaseService: IPurchaseService {
var baseURL: URL?
var apiPath = "/purchase/"
var apiClient: IAPIClient?
func perform(with objectId: String, then completion: (_ success: Bool) -> Void) {
guard let apiClient = self.apiClient, let url = self.baseURL else {
fatalError("Trying to do something with uninitialized purchase service")
}
let purchaseURL = baseURL.appendingPathComponent(self.apiPath).appendingPathComponent(objectId)
let urlRequest = URLRequest(url: purchaseURL)
self.apiClient.post(urlRequest) { (_, error) in
let success: Bool = (error == nil)
completion( success )
}
}
}
:
class OrderViewController {
var purchaseService: IPurchaseService?
var purchaseId: String?
func didClickShopButton(_ sender: UIButton?) {
guard let purchaseService = self.purchaseService, let purchaseId = self.purchaseId else {
fatalError("Trying to do something with uninitialized order view controller")
}
self.purchaseService.perform(with: self.purchaseId) { (success) in
self.presenter(showOrderResult: success)
}
}
}
:
class ServiceAssembly: Assembly {
var purchaseService: IPurchaseService {
return define(init: PurchaseService()) {
$0.baseURL = self.apiV1BaseURL
$0.apiClient = self.apiClient
}
}
var apiClient: IAPIClient {
return define(init: APIClient())
}
var apiV1BaseURL: URL {
return define(init: URL("http://someapi.com/")!)
}
}
:
var orderViewAssembly: Assembly {
var serviceAssembly: ServiceAssembly = self.context.assembly()
func inject(into controller: OrderViewController, purchaseId: String) {
define(init: controller) {
$0.purchaseService = self.serviceAssembly.purchaseService
$0.purchaseId = purchaseId
}
}
}
ViewController.
( )
ObjectGraph
- . , . , . A,B C A->B->C.( RetainCycle, ).
class A {
var b: B?
}
class B {
var c: C?
}
class C {
var a: A?
}
Assembly A.
class ABCAssembly: Assembly {
var a:A {
return define(init: A()) {
$0.b = self.B()
}
}
var b:B {
return define(init: B()) {
$0.c = self.C()
}
}
var c:C {
return define(init: C()) {
$0.a = self.A()
}
}
}
var a1 = ABCAssembly.instance().a
var a2 = ABCAssembly.instance().a
.
Singleton
, , , . Singleton SharedInstance , .. . EasyDi scope: singleton. , EasyDi , . B .
class ABCAssembly: Assembly {
var a:A {
return define(init: A()) {
$0.b = self.B()
}
}
var b:B {
return define(scope: .lazySingleton, init: B()) {
$0.c = self.C()
}
}
var c:C {
return define(init: C()) {
$0.a = self.A()
}
}
}
var a1 = ABCAssembly.instance().a
var a2 = ABCAssembly.instance().a
, .. B .
Prototype
. ABC A- :
class ABCAssembly: Assembly {
var a:A {
return define(scope: .prototype, init: A()) {
$0.b = self.B()
}
}
var b:B {
return define(init: B()) {
$0.c = self.C()
}
}
var c:C {
return define(init: C()) {
$0.a = self.A()
}
}
}
var a1 = ABCAssembly.instance().a
var a2 = ABCAssembly.instance().a
, 4 A
, . , .
( )
. EasyDi Assemblies. , , . :
let context: DIContext = DIContext()
let assemblyInstance2 = TestAssembly.instance(from: context)
, Assemblies .
class FeedViewAssembly: Assembly {
lazy var serviceAssembly:ServiceAssembly = self.context.assembly()
}
— , . . . , .
(objc.io #15 ). :
protocol ITheObject {
var intParameter: Int { get }
}
class MyAssembly: Assembly {
var theObject: ITheObject {
return define(init: TheObject()) {
$0.intParameter = 10
}
}
}
let myAssembly = MyAssembly.instance()
myAssembly.addSubstitution(for: "theObject") { () -> ITheObject in
let result = FakeTheObject()
result.intParameter = 30
return result
}
theObject intParameter.
A / BA / B
a/b . :
let FeatureAssembly: Assembly {
var feature: IFeature {
return define(init: Feature) {
...
}
}
}
let FeatureABTestAssembly: Assembly {
lazy var featureAssembly: FeatureAssembly = self.context.assembly()
var feature: IFeature {
return define(init: FeatureV2) {
...
}
}
func activate(firstTest: Bool) {
if (firstTest) {
self.featureAssembly.addSubstitution(for: "feature") {
return self.feature
}
} else {
self.featureAssembly.removeSubstitution(for: "feature")
}
}
}
, / .
VIPERVIPER
, , - . — VIPER, ViewController Presenter, ViewController.
EasyDi ‘’ . :
lass ModuleAssembly: Assembly {
func inject(into view: ModuleViewController) {
return define(key: "view", init: view) {
$0.presenter = self.presenter
}
}
var view: IModuleViewController {
return definePlaceholder()
}
var presenter: IModulePresenter {
return define(init: ModulePresenter()) {
$0.view = self.view
$0.interactor = self.interactor
}
}
var interaction: IModuleInteractor {
return define(init: ModuleInteractor()) {
$0.presenter = self.presenter
...
}
}
}
ViewController inject, viewController. , inject. , inject.
200 , . Typhoon, - , Swift .
, , . .
1 , , use_frameworks, Swift .
:
'1.1.1'pod 'EasyDi', '~>1.1'
Swift 3/4,
iOS 8+.
iOS 7 — , .
- — XKCD.