🍎

[SwiftUI][TCA] 基礎編

2022/05/13に公開

概要

この記事ではTCA初心者の筆者が理解を深めていくために、
pointfreeco公式のサンプルアプリを基に理解しやすく整理していきます。
https://github.com/pointfreeco/swift-composable-architecture

今回からは1章として01GettingStartedの内容から整理していきます。

TCAとは

公式のソースとREADMEを見るのに加えて、
@yimajoさんが紹介している記事とiOSDCでの紹介動画を見ると、
概念としては大まかに理解出来ると思います。
https://qiita.com/yimajo/items/77c204ab091223f9cb14

またより詳細な情報は公式のGitHub Pagesで網羅されていますので、
このメソッドについて知りたいなどの場合は参考にしています。
https://pointfreeco.github.io/swift-composable-architecture/

今回扱うファイル

今回は公式サンプルのこちらのファイルです。
https://github.com/pointfreeco/swift-composable-architecture/blob/0.16.0/Examples/CaseStudies/SwiftUICaseStudies/01-GettingStarted-Counter.swift

Counter

ここではTCAの基礎中の基礎を学びます。
+と-のボタンを押すと数字が変わるシンプルな実装です。

State,Action

他のアーキテクチャでも同様の概念があると思いますが、
StateActionを以下のように定義します。

struct CounterState: Equatable {
  var count = 0
}
enum CounterAction: Equatable {
  case decrementButtonTapped
  case incrementButtonTapped
}

Environment

このアプリでは使用していないので、何も処理がありません。
Environmentについては01-GettingStartedでは使用していませんので、
使用している02-Effectsでまた詳しく記載します。

struct CounterEnvironment {}

Reducer

ReducerではActionをSwitch文で分けStateに値を更新していきます。
それと同時にTCAの独特な概念として、
実行すべきEffectを各Actionでreturnします。
Effectについてはこのカウンターアプリでは.noneを返しており、
これはEmptyのEffectのことです。
後ほどEmptyではない返り値があるアプリ内で詳しく掘り下げます。

let counterReducer = Reducer<CounterState, CounterAction, CounterEnvironment> { state, action, _ in
  switch action {
  case .decrementButtonTapped:
    state.count -= 1
    return .none
  case .incrementButtonTapped:
    state.count += 1
    return .none
  }
}

View,Store

ここでSwiftUIのViewでStoreを参照できるよう、
WithViewStoreを使用します。
これ自体はSwiftUIのViewを返しているのでViewとなります。
WithViewStoreの引数にbodyの外で定義したStoreを渡すことで、
viewStore型でクロージャ内で使用することが出来ます。
このviewStoreに変換する意味合いとしては、
UIからのイベントをsendメソッドでActionとして送ることが出来ます。

struct CounterView: View {
  let store: Store<CounterState, CounterAction>

  var body: some View {
    WithViewStore(self.store) { viewStore in
      HStack {
        Button("−") { viewStore.send(.decrementButtonTapped) }
        Text("\(viewStore.count)")
          .font(.body.monospacedDigit())
        Button("+") { viewStore.send(.incrementButtonTapped) }
      }
    }
  }
}

次回

次回はpullback・combine・scopeといったメソッドの使用を理解していきます。
記事はこちら

Discussion