【UIKit版】The Composable Architecture(TCA)のカウンターサンプルを理解する
TCAはSwiftUIとの親和性が高いですが、UIKitでも実装が可能です。
TCAではUIKitによる実装サンプルも提供されているので、今回はUIKit版のカウンターサンプルを使ってUIKitでTCAを実装する方法について学びます。
UIKitCaseStudies/CounterViewController
SwiftUI版のカウンターサンプルの解説はこちらをご参照ください。
State、Action、Environment、Reducerの実装はSwiftUI版と全く同じ
SwiftUI版のカウンターサンプルと見比べるとわかるのですが、StateなどのTCAの各コンポーネントの実装は全く同じです。
これらのコンポーネントがUIKitでもSwiftUIでも同じように実装できるということは、UIKitで作られたアプリにあとからSwiftUIとTCAを導入して連携させることも比較的容易なのではないかと妄想しています(まだやったことないのでどのくらいの難易度なのか、そもそもできるのかとかはわかってません)。
StateのサブスクライブとActionのトリガーの設定
UIKitのビュー(UIViewController)でStateをサブスクライブする方法とActionのトリガーを設定する方法について見ていきます。
どちらもViewStoreを介して行うのはSwiftUI版と同じです。UIViewControllerのイニシャライザでStoreを受け取り、ViewStoreを生成してUIViewControllerのプロパティに格納しておきます。
final class CounterViewController: UIViewController { let viewStore: ViewStore<CounterState, CounterAction> var cancellables: Set<AnyCancellable> = [] init(store: Store<CounterState, CounterAction>) { self.viewStore = ViewStore(store) super.init(nibName: nil, bundle: nil) }
Stateをサブスクライブするときは、ViewStoreのpublisher
プロパティを参照します。ここではpublisher
はCounterState型のOutputを持っています。
self.viewStore.publisher .map { "\($0.count)" } .assign(to: \.text, on: countLabel) .store(in: &self.cancellables)
カウンターのプラスボタンタップ時にActionをトリガーする処理は典型的なTarget-Actionパターンで実装されています。
incrementButton.addTarget(self, action: #selector(incrementButtonTapped), for: .touchUpInside) ... @objc func incrementButtonTapped() { self.viewStore.send(.incrementButtonTapped) }
Actionが処理される流れ
Actionがトリガーされたあとの流れはSwiftUI版と同じなので割愛します。
こちらの記事をご参照ください。
まとめ
ざっとですがカウンターサンプルを例にUIKitでTCAを実装する方法について説明しました。
TCAはSwiftUIでもUIKitでもどちらでも実装できるように考慮されているので、UIKitで作られたアプリにあとからSwiftUIとTCAを導入し、Stateなどは両方のアーキテクチャで共有するといったことができそうです。
私の観測範囲ではそういった事例はまだ出てきて無さそうですが、今後TCAが広まって様々な導入事例が出てくることに期待したいですね。