Slide 1

Slide 1 text

Overviewing TCA v1.7 & Back and forth with MVVM potatotips #86 2024/02/07 Fumiya Sakai

Slide 2

Slide 2 text

自己紹介 ・Fumiya Sakai ・Mobile Application Engineer アカウント: ・Twitter: https://twitter.com/fumiyasac ・Facebook: https://www.facebook.com/fumiya.sakai.37 ・Github: https://github.com/fumiyasac ・Qiita: https://qiita.com/fumiyasac@github 発表者: ・Born on September 21, 1984 これまでの歩み: Web Designer 2008 ~ 2010 Web Engineer 2012 ~ 2016 App Engineer 2017 ~ Now iOS / Android / sometimes Flutter

Slide 3

Slide 3 text

iOSのUI実装本を執筆しています! 書籍に掲載したサンプルのバージョンアップや続編等に現在着手中です。 少しの工夫で実現できるTIPS集やライブラリ表現の活用集をはじめとした、iOSア プリ開発の中でも特にUI実装やUIKitを利用した画面の中で特徴を与える様な表現 という題材に焦点を当てた書籍となっております。 現在は電子書籍版のみとなります。 こちらは全て¥1,000となっております。 https://just1factory.booth.pm/ 概要: https://book-tech.com/ 価格: 📖 Booth 📖 Book Tech

Slide 4

Slide 4 text

UI実装であると嬉しいレシピブックの最新情報 UI実装であると嬉しいレシピブックVol.3として昨年10月に商業化しました! Still WIP これまでの同人誌として頒布したものに加えて、Vol.1及びVol.2に頒布したものの 中で書籍に載せきれなかったものや表現や動きが特徴的でユーザーにもほんの少し 遊び心を与える様なUI実装を紹介したものをVol.3としています。 概要: これからの構想: こちらで購入可能です: Amazon / Google Play / Apple Books / KINOKUNIYA / Rakuten BOOKS etc.. 🏊 iOS: SwiftUIを利用したUI実装や動画関連の実装 🏊 Android: Jetpack Composeの基本やその他気になるUI表現の考察

Slide 5

Slide 5 text

今回のスライドにつきまして 個人的にTCAに関連するトピックスや実際に軽く触れてみた際の所感を伝えたい 僕自身もTCAの熟練度についてはそれ程高くはないのでお手柔らかにお願いします。 1. 以前にも業務や個人活動の中でReduxを利用した経験があり以前からも関心はあった: Reduxの機構を利用したアーキテクチャを利用した開発経験を通じて、特に複雑かつ構成要素が複雑な入力を伴う画面等において は力を発揮する場面もありました。Reduxの考え方を採用しているTCAの動向は個人的にも気になっています。 2. MVVM ⇔ TCA or Reduxの処理を置き換える経験をした際に感じてた事: 以前の経験でMVVMで作られてたものをReduxへ置き換える、あるいはその逆をする経験をした際に、どの様に考えると結構わかり やすかったかという観点についても簡単ではありますがご紹介できると思います。 3. 最新のTCA(v1.7系)のドキュメントやサンプルに触れた際の所感: 最近あまりキャッチアップできずじまいのまま過ごしていました。iOS17からは「Observation Framework」の登場もあり、TCAに も変更があったので久しぶりに見てみると興味深い点もありましたので簡単に紹介できればと思います。

Slide 6

Slide 6 text

まずはSwiftUIとReduxを利用したサンプル実装例(1) SwiftUI+Reduxを利用したUI実装サンプルにおけるポイント解説: https://zenn.dev/fumiyasac/articles/01f1bc86bf8c40 以前に自作したサンプル実装例における概略図解と副作用に関する処理図解

Slide 7

Slide 7 text

まずはSwiftUIとReduxを利用したサンプル実装例(2) GitHub Example: https://github.com/fumiyasac/SwiftUIAndReduxExample 簡単なasync/awaitを利用したAPI処理やお気に入り処理を盛り込んでいます

Slide 8

Slide 8 text

Viewにおける状態変化と更新手段は共通点がある Actionを発行して副作用を伴うReducer処理で新たなStateを作成する流れは同様 1. ReduxでのView更新までの流れ: 2. TCAでのView更新までの流れ: Unidirectionalなデータの流れを作る方針はとても類似しているが副作用に関する考え方が特徴的に感じる。 View要素から実行された Actionを発行する Middleware(副作用)が 処理前後で実行される 該当するAction合致時は 内部処理を利用して別の Actionを発行する Reducer処理内でState内 のPropertyを更新する Middleware(副作用)がな い場合は直接Reducerへ 全体のStateが更新され View要素を更新する View要素から実行された Actionを発行する Effect(副作用)が Reducer内で実行される Reducer内処理において Effectを利用して内部で 別のActionを発行する Reducer処理内でState内 のPropertyを更新する Effect(副作用)がない場 合は直接Reducerへ 全体のStateが更新され View要素を更新する

Slide 9

Slide 9 text

case .fetchRecentNews: // (省略)Loading状態を表現するためにStateの内容を更新する // (処理例)API経由で最新情報データを取得する処理 return .run { send in await send( .fetchRecentNewsResponse( Result { try await self.newsRepository.fetchRecent() } ) ) } ReduxのMiddlewareとTCAのEffectのイメージを整理 APIリクエスト結果で成功・失敗のAction発行からStateの更新処理における事例 case let .fetchRecentNewsResponse(.success(response)): // (省略)成功時の状態を表現するためにStateの内容を更新する return .none case .fetchRecentNewsResponse(.failure): // (省略)失敗時の状態を表現するためにStateの内容を更新する return .none 同じReducer内で .send(…) を実行してActionを発行  @Dependency経由で取得したRepositoryの処理を実行 func resentNewsMiddleware() -> Middleware { return { state, action, dispatch in switch action { case let action as FetchRecentNewsAction: requestFetchRecentNews(action: action, dispatch: dispatch) default: break } } } Task { @MainActor in do { let response = try await NewsRepositoryFactory.create().fetchRecent() dispatch(SuccessFetchRecentNewsResponseAction(response)) } catch APIError.error(let message) { dispatch(FailureFetchRecentNewsResponseAction()) } } } 1. TCAのEffectを利用した処理: 2. ReduxのMiddlewareを利用した処理: Middleware関数内でRepositoryをインスタンス化して処理を実行 関数内で dispatch() を実行してActionを発行 private func requestFetchRecentNews(action: FetchRecentNewsAction, dispatch: @escaping Dispatcher)

Slide 10

Slide 10 text

自作したRedux処理からTCAだったらなと感じた点 アーキテクチャ部分の提供だけではなくかゆい所に手が届く様な配慮がある 1. Reduxを自作した際のつらみ?の部分: 2. もしTCAだと嬉しく感じる部分: 両方を試した事で「この部分を提供してくれるのは本当にありがたい」と感じる事は多かった。 Storeを分割できる機構を持っている .scope(state: \●●●, action: \◆◆◆) で制限する Stateの親子関係を意識したState構造 特に異なる画面にStoreを渡したい様な場合には、実装処理や構造を利 用してうまく範囲を制限したりする等の配慮が必要 @EnvironmentObjectを利用したDI機構 APIリクエストやデータ永続化処理をそれぞれMiddlewareに分割して Storeに定義するので、ファイルが多くなると結構大変な印象 Swift Concurrencyのサポート対応 async / awaitでの処理機構・@Sendableへのケアが自前で必要 Viewにおいて直接Store全体を観察する形にすると、ある状態が変化し た場合、SwiftUIはすべてのUI更新を要求するため全体を再計算する DI機構・APIClient等をはじめとしたサポートが充実 @Dependency / liveValue / point-free製のOSS等 PropertyWrapperの形で提供されている便利な機構や自前で作成すると 大変そうな部分や処理等を提供してくれている

Slide 11

Slide 11 text

元々MVVMで作成された画面をTCAに置き換えるアイデア どちらも共に良いと思うが(今回はあえて)置き換える事を考えてみます ① 初期状態 : メールマガジン登録をするための画面: メールを送信する メールマガジン登録 📩 Mail: [email protected] メールを送信する メールマガジン登録 📩 Mail: abcdefc123456 Invalid. メールマガジン登録処理に関する仕様 この時はメールを送信するためのボタンは非活性状態となる ② 入力中の状態 : 形式が正しくない場合のボタンは非活性状態となる テキストフィールドの左下にエラーメッセージが表示される ③ 入力完了の状態 : 形式が正しい場合のボタンは活性状態となる テキストフィールドの左下にエラーメッセージが表示されない ※ 目のボタンを押下すると入力内容がクリアされる 👀 👀

Slide 12

Slide 12 text

SwiftUIでのMVVM構成に関するポイントをまずは確認 MVVM構成を考える際はViewModel内部実装とSwiftUIのStateに関する事 1. 基本方針と構成のおさらい : View Components ViewModel UseCase・Repository Infrastructure ※ AndroidではStateもViewと分離 Button活性・非活性状態を管理する: ※基本的にはasync/awaitでLogicを作成して、Viewとの連結時に必要に合わせてCombineで補う方針 2. SwiftUIのView要素の特徴を踏まえてポイントを整理する : ① SwiftUIのVIew要素ではStateが含まれる形にそもそもなっている ② ViewModelはObservableObjectを継承し、View要素では @ObservedObject / @StateObject で連結する @Published private(set) var sendButtonDisabled: Bool = true @Published var inputEmail: String = "" 入力用TextFieldと連結する: ※ Bindingで渡す必要があるため Input Output ViewModel内に定義したメソッド: viewModel.doSomething() ViewModel内の@Publishedで定義したProperty 双方向Bindingの様な形のイメージ

Slide 13

Slide 13 text

MVVMでの処理をTCAに置き換える際のイメージ(1) Inputは定義したメソッドの実行・Outputは@Publishedで定義した変数 ① 入力するTextField要素とBindingするProperty : メールを送信する メールマガジン登録 📩 Mail: [email protected] ViewModel処理における組み立て方のヒント ※ didSet {…} を利用して関連する値を更新したり、Validation処理を実行させる様にするのがポイント メールマガジン登録画面をMVVMで実装する場合のViewModelにおける見通し: 👀 @Published var inputEmail: String = "" ② Button要素の活性状態をHandlingするProperty : ※ Input用のメソッドやTextFieldの入力状態によってこの値が変化する様に調整する @Published private(set) var sendButtonDisabled: Bool = true 双方向Binding前提の処理 ③ 送信要素を押下した時の処理実行メソッド : func postInputEmail() { … 入力データをPOSTで送信する … } Output Input ※ APIリクエストやデータ永続化の様なBusiness Logic(Domain Logic)の処理を実行する

Slide 14

Slide 14 text

MVVMでの処理をTCAに置き換える際のイメージ(2) ViewModelに定義したInput・Outputをヒントにして置き換えていくと良さそう ① @Published で定義したOutput用のPropertyがStateのヒントになり得る : メールを送信する メールマガジン登録 📩 Mail: [email protected] この画面で利用すつStateを定義する 入力TextFieldと連動する & Button状態のHandling用のProperty → StateのPropertyになる? メールマガジン登録画面をTCAで実装する場合のポイントになりそうな部分: 👀 Reducer処理とAction名を定義する State ② Action名はInput用のメソッド名がヒントになり得る : Recucerに定義している各種case名(すなわちAction)は @Published を更新するためのメソッ ド名を命名や内容を参考に置き換えてみる Dependencyを利用する事でBusiness Logic(Domain Logic)の定義を利用可能にする Reducer Action @Dependency(\.mailMagazineRepository.postInputEmail) var postInputEmail 入力データをPOSTで送信する

Slide 15

Slide 15 text

Observationの登場とTCAの最新動向をおさらいする(1) Observationへの対応や既存の書き方が変化する等以外と変更があると感じた v1.7以降ではObservationをベースにした形でViewStoreが不要になったの変更点の一番HotなTopic : ① GitHubのREADMEでの実装事例も更新されている : Observationの変更がメインではあるが、PropertyWrapper関連の変更や記載の変更等も含まれていました。 ② 変更点の一覧は「Migrating to 1.7」を参照 : Observation利用Versionのコード差分が紹介されています。 README記載のもの以外での変更点も色々紹介されています。

Slide 16

Slide 16 text

Observationの登場とTCAの最新動向をおさらいする(2) データ永続化を利用しないTODOリストのExampleですが基本が詰まっている Reducerに関する変更点やView要素との連携方法が一通り学べそう :

Slide 17

Slide 17 text

Observationの登場とTCAの最新動向をおさらいする(3) 目次を眺めてみると「Replacing … 」の項目が結構多くある印象でした よりコードとして自然な書き方を目指している様にも見受けられます : 「Migrating to 1.7 より」項目を抜粋してみました:

Slide 18

Slide 18 text

まとめ TCAの変化スピードは早く激しいが興味深い点も多く楽しいと感じている 1. Reduxの流れを汲む点と副作用に対する考え方を知ると理解がし易いと思います: Unidirectional(単方向)なデータの流れを実現する基本的な流れについては、共通点も多い印象がありますが、その一方で副 作用的な処理をする機構については考え方が大きく異なる点を知る事が、最初の理解を深める足掛かりになると思います。 2. MVCやMVVMでの実装からどの様に整理と分解をするかを考えてみるのも1つの手かと思います: 構成だけを一見すると全く別物の様に思いますが、InputとOutputのPropertyやMethodを整理していくと、TCAで必要な要素であ る「Action / Reducer / State」へ置き換えて考える事の手助けになる場合も多いと思います。 最近業務でも少し触れる機会があったので、簡単ではありますが所感をまとめました。 3. TCAは全体的にAppleが提供しているAPIを使うための良い形を模索している様に見えました: スピーディーかつ大きな変更やバージョンアップも頻繁にあるものの、まさしく「かゆい所に手が届く」対処が施されている点 やより便利かつ直感的な形を実現できる様に、最新iOSの変更も積極的に取り組んでいる点は本当に興味深く感じております。

Slide 19

Slide 19 text

Thank you for listening ! 今回は本当にエッセンスだけの紹介になりましたが、TCAのキャッチアップはとても楽しいものです。 改めてUI実装サンプル実装&記事化できる様に進めていければと思います!