【iOS/Xcode/Swift】SwinjectでDI(依存性注入)を実装する方法と使い方

【iOS/Xcode/Swift】SwinjectでDI(依存性注入)を実装する方法と使い方

この記事からわかること

  • iOS/XcodeDI(依存性注入)の実装方法
  • Swinject使い方

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

環境

DI(Dependency Injection:依存性注入)とは?

DI(Dependency Injection)」は「依存関係をオブジェクト外に持たせて外部から注入するような設計にする」デザインパターンの1つです。日本語では依存性注入という意味になります。詳細は以下の記事を参考にしてください。

Swinjectとは?

公式リファレンス:Swinject

Swinject」はSwift向けの軽量な依存性注入(DI)ライブラリです。類似のライブラリとしてStoryboard向けの依存性注入(DI)ライブラリSwinjectStoryboardも用意されていますので活用してみてください。

ライブラリの導入

SwinjectはCocoa Pods、Swift Package Manager、Carthageをサポートしているのでよしななものを使用して導入してください。


pod 'Swinject'

Swift Package Managerなら以下で検索してください。

https://github.com/Swinject/Swinject.git

Swinjectの使用例

依存性注入を行う主な方法は2つありますがSwinjectでは主にコンストラクタインジェクションを使って行きます。。

コンストラクタインジェクション

コンストラクタインジェクションはその名前の通りコンストラクタを使用して依存性を挿入する方法です。ViewModelやRepositoryなどシンプルな独自のクラスなどでは簡単にコンストラクタインジェクションを実装することができます。

フィールドインジェクション(セッターインジェクション)

AndroidでのFragmentやActivityなどシステムによってインスタンス化されるクラスはコンストラクタインジェクションが不可能です。そのためクラスの作成後に依存関係がインスタンス化されます。

DIコンテナを作成する

SwinjectではDIコンテナと呼ばれるものをオブジェクトを使用して依存性注入を管理しています。DIコンテナはContainerクラスとして管理されています。このクラスに対してregisterメソッドを使用して依存元と依存先をマッピングを定義します。

import Swinject

final class DIContainer: @unchecked Sendable{
    static let shared = DIContainer()

    private let container = Container() { c in
        // DIコンテナに依存元と依存先をマッピングして定義する
        c.register(Repository.self) { _ in RepositoryImpl() }
        c.register(ViewModel.self) { r in
            ViewModel(repository: r.resolve(Repository.self)!)
        }
    }

    public func resolve<T>(_ type: T.Type) -> T {
        guard let resolved = container.resolve(type) else {
            fatalError("依存解決に失敗しました: \(type)")
        }
        return resolved
    }
}

DIコンテナの中から対象のクラスを取得する必要があるのでどこからでもアクセスできるようにresolveメソッドを使って対象のクラスを取得できるようなメソッドを定義します。

DIコンテナの中から対象のクラスを取得するにはresolveメソッドを使用します。引数に取得したいデータ型を指定します。

// DIコンテナから対象のクラスを取り出して使用する
let viewModel: ViewModel = DIContainer.shared.resolve(ViewModel.self)

コンストラクタインジェクション

例えばViewModelやRepositoryなどの場合に依存性注入(DI)を行うためには「コンストラクタインジェクション」という方法を用います。まずは必要となる各クラスを定義していきます。

リポジトリプロトコル

protocol Repository {
    func fetchMessage() -> String
}

リポジトリ実体クラス

class RepositoryImpl: Repository {
    func fetchMessage() -> String {
        return "こんにちは!これはRepositoryからのメッセージです。"
    }
}

ViewModel

class ViewModel: ObservableObject {
  
  private let repository: Repository
  
  @Published  private(set) var message: String = ""

  init(repository: Repository) {
      self.repository = repository
  }

  func loadMessage() {
      message = repository.fetchMessage()
  }
}

ここまで準備ができたら依存性注入を行うためDIコンテナを作成します。先ほど定義したDIコンテナがそのまま使えるので定義しておいてください。あとはresolveメソッドで参照できます。

struct RootView: View {
    // DIContainer から注入した ViewModel を使う
    @StateObject private var viewModel = DIContainer.shared.resolve(ViewModel.self)
    
    private func greeting() {
        viewModel.loadMessage()
    }
    var body: some View {
        VStack {
            Button {
                greeting()
            } label: {
                Text(viewModel.message)
            }
        }
        .padding()
    }
}

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

Search Box

Sponsor

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article