Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
KMPプロジェクトでTCAを使わず、iOSのUIアーキテクチャを自作した話
Search
Nao
October 08, 2026
0
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
KMPプロジェクトでTCAを使わず、iOSのUIアーキテクチャを自作した話
Nao
October 08, 2026
More Decks by Nao
See All by Nao
Kotlin Multiplatform + iOS アーキテクチャの実践
nao_rmt
0
210
Featured
See All Featured
エンジニアに許された特別な時間の終わり
watany
109
250k
Neural Spatial Audio Processing for Sound Field Analysis and Control
skoyamalab
0
570
A designer walks into a library…
pauljervisheath
211
25k
Faster Mobile Websites
deanohume
310
32k
Thoughts on Productivity
jonyablonski
76
5.4k
Paper Plane
katiecoart
PRO
5
54k
Navigating the moral maze — ethical principles for Al-driven product design
skipperchong
2
630
Impact Scores and Hybrid Strategies: The future of link building
tamaranovitovic
0
460
The Straight Up "How To Draw Better" Workshop
denniskardys
239
140k
The SEO Collaboration Effect
kristinabergwall1
1
590
The AI Search Optimization Roadmap by Aleyda Solis
aleyda
2
6.3k
Gemini Prompt Engineering: Practical Techniques for Tangible AI Outcomes
mfonobong
2
590
Transcript
KMPプロジェクトでTCAを使わず、 iOS向けUIアーキテクチャを自作した話 10/8 DroidKaigi / iOSDC アフターイベント 01 / 21
自己紹介 権奈悟 こん なお @nandy_pj ディップ株式会社 新卒3年目 iOSエンジニア 趣味 筋トレ・Netflix・韓国アイドル
02 / 21
フルリニューアルの始まり 2025年6月、バイトルアプリのフルリニューアルが始まった 当時のiOSメンバーは、私とテックリードの2人 KMPプロジェクトのiOSのUIをどう設計するか、 2人で考えていた 03 / 21
先に結論 実装で出た課題に合わせて、 プロダクトに合う構成へ変えてきた 判断の理由は、ADRや実装ルールに残す 04 / 21
アプリ全体の 構成 Domain層とData層を、 KMPで共有 Presentation層は、各OS で実装 05 / 21
今回話す範囲 iOSのUI(Presentation層) iOSの状態管理と、KMP への接続 06 / 21
SwiftUIの状態管理を、まずはMVVMで SwiftUIは状態に応じてUIを描画する(Jetpack Composeにも共通する考え方) 状態をどこに持ち、操作を受けてどう更新するかを設計する View SwiftUI 操作 状態 ViewModel 表示用の状態と処理
呼ぶ 結果 Model データ・ 業務処理 MVPはMVVMで実装を始めた 移行先の候補と、移行しやすい書き方は先に決めておいた 07 / 21
ViewModelが大きくなった View SwiftUI 操作 状態 ViewModel 基本情報の状態・処理 自己PRの状態・処理 職務経験の状態・処理 写真の状態・処理
呼ぶ 結果 Model データ・ 業務処理 状態が複数のプロパティに分かれ、 変更の影響を追いにくくなった 08 / 21
単方向の状態管理の選択肢:TCA The Composable Architecture Point-Freeが開発するSwiftのオープンソースライブラリ。 単方向の状態管理、副作用管理、機能の合成、テストを支える 要素 役割 State Action
Reducer Store 画面の表示や処理に使う状態 タップ、通信結果などのイベント 状態を更新し、実行する副作用を返す Actionを受け、Reducerや副作用を実行する 09 / 21
TCAを採用しなかった理由 01 02 OSの更新や、ライブラリの方針変更・開発 終了に、自分たちだけでは対応できない可 能性がある 独自の概念が多く、新しく入るメンバーが 慣れるまでに時間がかかる 外部ライブラリへの依存 学習コストの高さ
Swiftの標準機能の範囲で、単方向の考え方だけを取り入れた 10 / 21
テックリードの提案で、単方向アーキテクチャへ移行 01 状態を1つにまとめる 02 Androidと似た構成にする 03 Swiftの標準機能だけで作る 操作から更新までを追えるようにする AndroidではMVIを採用。Android開発者も同じ考え方で読めるようにする Observationとasync/awaitを使う
11 / 21
View・Intent・Store・ViewState 操作 View SwiftUIの表示 操作を受ける send Intent 操作・イベント を表す 更新
Store 処理を実行し、 状態を更新する ViewState 表示用の状態 Viewは状態を読み、変更に応じて表示を更新する 12 / 21
TCAと似ているところ 役割 私たちの構成 TCA 状態 操作・イベント 状態更新・非同期処理 ViewState Intent Store内のメソッド
State Action Reducer / Effect / Store 状態・操作・更新処理を分けると、似た役割が出てきた 副作用管理や、機能を合成する仕組みは異なる 13 / 21
StoreとKMPをつなぐBridge Storeからは、Swiftの窓口を通してKMPを呼ぶ Store 処理を頼む 処理を依頼し、 結果で状態を更新 結果を返す Bridge SwiftのProtocol async
throwsに変換 共有処理を呼ぶ 結果を返す KMP Command Query Featureモジュールは、Protocolだけに依存し、KMPを直接importしない テストはモックに差し替え、KMPのビルドを待たずに回せる 14 / 21
「気になる」操作で見る処理の順番 担当 処理 View Intentを送る Store 連打を防ぎ、Bridge経由で Commandを呼ぶ KMP DBとAPIを更新し、結果を返す
Store 結果に応じてViewStateを更新する 上限超過なら、警告トーストを表示 追加前 追加後 15 / 21
実装して見えてきた課題:非同期テスト フルリニューアルでは、画面を実装しながら設計も見直せた。その一例が非同期テスト Storeが起動したTaskの完了を、 テストは状態の変化のポーリングで待っていた CIが混むと失敗 Test iOS App / Test
iOS App (pull_request) 16 / 21
EffectTrackerを導入 Storeが起動した副作用を登録し、テストはすべて終わるまで待つ Store.swift 1 // 副作用のTaskを登録して起動する 2 effects.track(Task { await
bridge.execute() }) StoreTests.swift 1 // 登録された副作用がすべて終わるまで待つ 2 await store.send(.screenAppeared) 3 await effects.waitUntilIdle() TCAのTestStoreと同じ発想を、この1点だけ取り入れた 17 / 21
判断の理由をADRに残す ADR:アーキテクチャの意思決定記録 なぜこの構成にしたか、何を比較したかを記録する 後から入るメンバーへ コードだけでは分からない 判断の背景を伝える 実装を支援するAIへ AGENTS.mdと領域別の実装ルールを Claude Code・Codexで共有する
18 / 21
新しく入る人が理解しやすい設計へ 構成や責務が分かり、 実装の進め方に迷わないようにしたい 初学者向けの学習スキルを作って運用中 コードとADRを根拠に、AIがアーキテクチャを段階的に教える 構成図を描く・1つの操作を画面からAPIまで追う・理解度クイズ 19 / 21
残っている課題 Storeの肥大化 例 ホームは、多くのセクションを1つのStoreで扱っていて、処理が集まりやすい どこで分けるかの基準は、まだ決められていない 自作で足りなくなったら、自作を広げずにTCAを検討する 20 / 21
まとめ 課題・目的 対応 ViewModelで状態を追いにくい テストのたびにKMPのビルドを待つ 非同期テストが不安定 人・AIに判断の背景を伝えたい MVVMから単方向アーキテクチャへ CoreInterfaceを分離 EffectTrackerを導入
ADR・実装ルールを記録 実装の課題に合わせて、プロダクトに合う構成へ改善してきた 21 / 21