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.



All Articles