👼

TCAを初心者が約3ヶ月使ってみた

2024/12/24に公開

はじめに

こんにちは!さきです!
この記事はCA Tech Lounge Advent Calendar 2024の24日目、クリスマスイブの記事になります🎄
Swiftのアーキテクチャの一つであるTCA(The Composable Architecture)をCA Tech Loungeに入ってから使い始めたので、Swift初心者が使ってみてどうなったかの記事になります。
間違えている点があれば教えてください!

TCAって?

TCAは、Appleのプラットフォーム(iOS、macOSなど)向けのアプリケーションアーキテクチャを提供するオープンソースフレームワークです。一貫性があり、明確で、再利用可能かつテスト可能な設計 を目指しています。

TCAは、Reduxなどのアーキテクチャから強い影響を受けています。
Reduxは、主にReactなどのJavaScriptフレームワークで使われる、アプリケーションの状態管理のためのライブラリで、TCAの設計思想の基礎となっています。

公式のREADMEによると、

The Composable Architecture (省略すると TCA) は、コンポジション、テスト、開発者にとっての使いやすさを考慮し、一貫性のある理解しやすい方法でアプリケーションを構築するためのライブラリです。

TCAは大きくわけて、主にState、Action、Reducer、Storeの4つの要素に分かれます。
アプリの複雑な状態管理をシンプルに行うことができ、ユーザーの操作などのActionによって、状態を変化させ、状態が変更されるごとに新しい状態を返します。

State

状態の管理をしてくれる変数の格納場所
@ObservableStateはViewがStoreの状態を監視し、状態の変化に反応して自動的に再描画を行うTCAのマクロ

 @ObservableState
    struct State {
     var counter: Int = 0
    {

Action

アプリケーション内で発生するすべての操作やイベントを表現する役割を持つ
あらゆるイベントやユーザーの操作を表し、enumで定義する

 enum Action {
     case countUp
    }

Reducer

Viewから受け取ったアクションなどをもとに、アプリケーションの状態を変化させる方法を書いて新しい状態を返す純粋関数
非同期で実行されるEffectを返して外部とのやり取りをする
Effectについては以前スクラップをまとめましたが、副作用のことでAPIの呼び出しなど、同じ条件で呼び出しても同じ結果が返ってくるとは限らないもののこと

@Reducer
struct AppFeature {
 @ObservableState
    struct State {
     //省略
    }
    enum Action {
     //省略
    }
    var body: some ReducerOf<Self> {
         Reduce { state, action in
            switch action {
            case .countUp:
                counter += 1
                return .none
            }
        }
    }

Store

StateとReducerを保持しViewとやりとりをする窓口
Storeが更新されると、Viewが再描画される

var body: some View {
    AppView(store:
            .init(
                initialState: AppFeature.State(),
                reducer: {
                    AppFeature()
                }
            )
    )
}


struct AppView: View {
    let store: StoreOf<AppFeature>
    var body: some View {
        VStack{
            Text("\(store.counter)")
            Button {
                store.send(.countUp)
            } label: {
                Text("add")
            }
        }
    }
}

メリット、デメリット

メリット

状態がどのように変化するかがわかりやすく、状態変更がどこでどのように行われるかが明確で、どこで問題が発生したのかを追跡するのが非常に簡単でした。また、MVVMと比較して、ViewModelが肥大化する問題も解決することができました。

また、preview用にmockを簡単に注入できたので、previewで実際にAPIと通信することなく、表示の確認ができたり、エラー分岐ができたのがとても良かったです。
mockの注入などDependency Keyについてまとめたスクラップもあります。
TCAはテスタビリティを重視して設計されているため、テストを非常に簡単に行うことができます。 Reducerは純粋関数なので、同じ入力に対して常に同じ出力を返すことが保証されており、テストが容易です。また、TestStoreを使うことで、Actionの送信、Stateの変化、Effectの実行などを、モックや依存性の注入を用いて詳細に検証することができます。

デメリット

常にアップデートされているので、インターネット常にある情報がどんどん古くなってしまったので、バージョンが違うと、サンプル通りに書いても動かない!ということが多々ありました。
例えば、調べて出てきた記事を片っ端からやってみても、

let reducer = Reducer<State, Action, Environment> { state, action, environment in

のような以前の書き方のものが多く、初手から書き方がわからなくて諦めそうになりました。
AIに聞いても古い情報も出てきてしまうことが多いので、自力で公式ドキュメントを読んで試してみる以外に方法がなかったので、MVVM等のアーキテクチャに比べて、学習コストが高いなと感じました。

まとめ 初心者が使ってみて

最初の学習コストの高さで投げ出しそうになりました。また、書き方の正解が分からず、いまだに自分のコードが正解なのかもっと良い書き方があるのか不安な気持ちもあります。今はCA TechRoungeのiosの人たちと共同開発をしていて、そこでもTCAを使っているので、皆さんの書き方を吸収させていただいています。
メリットのところに書いたmockデータの注入が簡単にできる部分が個人的にとても好きで、以前はPreviewでAPI通信するのは良くないかなと思い、絡んでるところは使っていなかったので、SwiftUIのメリットであるPreviewを使いやすくなったのがすごく嬉しいです。
困ったら公式ドキュメントを読んだりすることで、何とかコードを理解してかけ始めているので、これからも使っていきたいです!間違えている点があれば教えてください!
みなさま良いクリスマスをー!🎅ありがとうございました!

Discussion