200行のコードを備えた効率的なSwift DIライブラリ

EasyDiライブラリには、Swiftの依存関係コンテナが含まれています。 このライブラリの構文は、迅速な開発と効率的な使用のために特別に設計されています。 200行に収まりますが、成人のDiライブラリに必要なものはすべて知っています。

-オブジェクトの作成と既存のオブジェクトへの依存関係の注入
-コンテナへの分離-アセンブリ
-依存関係を解決するタイプ:オブジェクトのグラフ、シングルトン、プロトタイプ
-循環依存関係の解決
-オブジェクトの置換とテストの依存関係テスト

EasyDiには、登録/分離の解決はありません。 代わりに、依存関係は次のように説明されます。

var apiClient: IAPIClient {
  return define(init: APIClient()) {
    $0.baseURl = self.baseURL
  }
}

Cocoapods / EasyDi
Github / EasyDi

« DI », :


DI ?( )


, 5 .

, DI :


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 / B

A / 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")
    }
  }
}

, / .

VIPER

VIPER



, , - . — 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.

Source: https://habr.com/ru/post/J331710/


All Articles