DITranquillityによる依存性注入

依存性注入は、システムを柔軟に構成し、このシステムのコンポーネントの相互依存関係を正しく構築できる、かなり一般的なパターンです。 入力のおかげで、Swiftでは、依存関係グラフを非常に簡単に説明できる便利なフレームワークを使用できます。 今日、これらのフレームワークの1つであるDITranquillity



について少しお話ししたいとDITranquillity



ます。







このチュートリアルでは、次のライブラリ機能について説明します。









コンポーネントの説明



アプリケーションは、 ViewController



Router



Presenter



Networking



主要なコンポーネントで構成されます-これらは、iOSアプリケーションで非常に一般的なコンポーネントです。







コンポーネント構造






ViewController



Router



は相互に周期的に導入されます。







準備する



最初に、Xcodeでシングルビューアプリケーションを作成し、CocoaPodsを使用してDITranquillityを追加します 。 必要なファイルの階層を作成し、2番目のコントローラーをMain.storyboardに追加し、StoryboardSegueを使用して接続します。 その結果、次のファイル構造が取得されます。







ファイル構造






次のように、クラスに依存関係を作成します。







コンポーネント宣言
 protocol Presenter: class { func getCounter(completion: @escaping (Int) -> Void) } class MyPresenter: Presenter { private let networking: Networking init(networking: Networking) { self.networking = networking } func getCounter(completion: @escaping (Int) -> Void) { // Implementation } }
      
      





 protocol Networking: class { func fetchData(completion: @escaping (Result<Int, Error>) -> Void) } class MyNetworking: Networking { func fetchData(completion: @escaping (Result<Int, Error>) -> Void) { // Implementation } }
      
      





 protocol Router: class { func presentNewController() } class MyRouter: Router { unowned let viewController: ViewController init(viewController: ViewController) { self.viewController = viewController } func presentNewController() { // Implementation } }
      
      





 class ViewController: UIViewController { var presenter: Presenter! var router: Router! }
      
      





制限事項



他のクラスとは異なり、 ViewController



は私たちによって作成されるのではなく、 UIStoryboard.instantiateViewController



実装内のUIKitライブラリによって作成されるため、ストーリーボードを使用して、初期UIViewController



を使用してUIViewController



の継承者に依存関係を注入することはできません。 そのため、 UIView



UITableViewCell



相続人がいUITableViewCell









プロトコルの背後に隠されたオブジェクトは、すべてのクラスに埋め込まれていることに注意してください。 これは、依存関係を実装する主なタスクの1つです。実装ではなく、インターフェイスに依存関係を作成します。 これは、将来、コンポーネントを再利用またはテストするための異なるプロトコル実装を提供するのに役立ちます。







依存性注入



システムのすべてのコンポーネントが作成された後、それらの間のオブジェクトの接続に進みます。 DITranquillityでは、出発点はDIContainer



、これはcontainer.register(...)



メソッドを使用して登録を追加します。 依存関係をパーツに分離するには、 DIFramework



DIPart



ます。これらは実装する必要があります。 便宜上、1つのApplicationDependency



クラスのみを作成します。このクラスは、 DIFramework



を実装し、すべての依存関係の登録場所として機能します。 DIFramework



インターフェイスでは、メソッドload(container:)



1つだけ実装する必要があります。







 class ApplicationDependency: DIFramework { static func load(container: DIContainer) { // registrations will be placed here } }
      
      





依存関係のない最も単純なサインアップから始めましょうMyNetworking









 container.register(MyNetworking.init)
      
      





この登録では、イニシャライザーを介した実装が使用されます。 コンポーネント自体には依存関係がないという事実にもかかわらず、コンポーネントの作成方法をライブラリに明確にするために初期化子を提供する必要があります。







同様に、 MyPresenter



MyRouter



登録しMyRouter









 container.register1(MyPresenter.init) container.register1(MyRouter.init)
      
      





注: register



は使用されず、 register



が使用されることに注意してください。 残念ながら、これは、オブジェクトの初期化子に依存関係が1つだけあるかどうかを示すために必要です。 つまり、0または2つ以上の依存関係がある場合は、 register



を使用するだけです。 この制限は、Swiftバージョン4.0以降のバグです。







ViewController



を登録します。 初期化子ではなく変数に直接オブジェクトを注入するため、登録の説明はもう少し多くなります。







 container.register(ViewController.self) .injection(cycle: true, \.router) .injection(\.presenter)
      
      





\.presenter



の形式の構文はSwiftKeyPathです。これにより、依存関係を簡潔に実装できます。 Router



ViewController



周期的に依存しているため、 cycle: true



で明示的に指定する必要がありcycle: true



。 ライブラリ自体は、明示的な指示なしにこれらの依存関係を解決できますが、この要件は、グラフを読んでいる人が依存関係チェーンにサイクルがあることをすぐに理解できるように導入されました。 ViewController.init



ではなく ViewController.self



ViewController.init



ていることにも注意してください。 これについては、上記の制限のセクションで説明しました。







また、特別な方法を使用してUIStoryboard



を登録する必要があります。







 container.registerStoryboard(name: "Main")
      
      





これで、1つの画面の依存関係グラフ全体について説明しました。 ただし、このグラフにはまだアクセスできません。 その中のオブジェクトにアクセスできるDIContainer



を作成する必要があります。







 static let container: DIContainer = { let container = DIContainer() // 1 container.append(framework: ApplicationDependency.self) // 2 assert(container.validate(checkGraphCycles: true)) // 3 return container }()
      
      





  1. コンテナを初期化する
  2. グラフの説明を追加します。
  3. すべてが正しく行われたことを確認します。 ミスをすると、依存関係の解決中にではなく、グラフの作成時にアプリケーションがクラッシュします


次に、コンテナをアプリケーションの開始点にする必要があります。 これを行うには、プロジェクト設定でMain.storyboard



を起動ポイントとして指定する代わりに、 didFinishLaunchingWithOptions



メソッドAppDelegate



実装します。







 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { window = UIWindow(frame: UIScreen.main.bounds) let storyboard: UIStoryboard = ApplicationDependency.container.resolve() window?.rootViewController = storyboard.instantiateInitialViewController() window?.makeKeyAndVisible() return true }
      
      





打ち上げ



最初の起動時にドロップが発生し、次の理由で検証が失敗します。









最初のエラーを修正するのは簡単です-コンテナ内のメソッドを使用できるプロトコルを指定できる特別なメソッドがあります。







 container.register(MyNetworking.init) .as(check: Networking.self) {$0}
      
      





登録を次のように説明しますMyNetworking



オブジェクトには、 Networking



プロトコルを介してアクセスできます。 これは、プロトコルの下に隠されているすべてのオブジェクトに対して実行する必要があります。 {$0}



コンパイラによる正しい型チェックのため{$0}



追加します。







2番目の間違いはもう少し複雑です。 いわゆるscope



を使用する必要があります。これは、オブジェクトが作成される頻度とオブジェクトがどれだけ存続するかを記述します。 循環依存関係に参加する登録ごとに、 objectGraph



と等しいscope



指定する必要があります。 これにより、コンテナに対して、毎回作成するのではなく、作成時に同じ作成済みオブジェクトを再利用する必要があることが明確になります。 したがって、判明します:







 container.register(ViewController.self) .injection(cycle: true, \.router) .injection(\.presenter) .lifetime(.objectGraph) container.register1(MyRouter.init) .as(check: Router.self) {$0} .lifetime(.objectGraph)
      
      





再起動後、コンテナは検証に成功し、作成された依存関係でViewControllerが開きます。 viewDidLoad



ブレークポイントを設定して確認できます。







画面間の遷移



次に、 SecondViewController



SecondPresenter



2つの小さなクラスを作成し、 SecondViewController



をストーリーボードに追加し、 "RouteToSecond"



という識別子を使用してそれらの間にSegue



を作成します。







新しいクラスごとにApplicationDependency



さらに2つの登録を追加します。







 container.register(SecondViewController.self) .injection(\.secondPresenter) container.register(SecondPresenter.init)
      
      





SecondPresenter



プロトコルの背後に隠さず、実装を直接使用するため、 .as



を指定する必要.as



ません。 次に、最初のコントローラーのviewDidAppear



メソッドで、 performSegue(withIdentifier: "RouteToSecond", sender: self)



を呼び出して、2番目のコントローラーを開き、 secondPresenter



依存関係をsecondPresenter



ます。 ご覧のとおり、コンテナーはUIStoryboard



から2番目のコントローラーを作成し、依存関係を正常に配置しました。







おわりに



このライブラリを使用すると、循環依存関係、ストーリーボードを簡単に操作でき、Swiftの自動型推論を完全に使用して、依存関係グラフを記述するための非常に短く柔軟な構文を提供します。







参照資料



githubライブラリの完全なサンプルコード







githubの DITranquillity







英語の記事








All Articles