iOS Architecture¶
目的¶
ios/Rikako のディレクトリ構成と責務を整理するためのメモです。
- 関連ドキュメント: README.md
- 関連ドキュメント: navigation.md
- 関連ドキュメント: onboarding.md
現在の方針は、画面から直接 API を呼ばず、次の依存方向で組むことです。
Screen (View)
-> ViewModel
-> UseCase
-> Repository
-> Infrastructure
ディレクトリ構成¶
ios/Rikako/
AppContainer.swift
AppState.swift
RikakoApp.swift
Domain/
Entity/
Repository/
UseCase/
Data/
Remote/
Request/
Response/
Repository/
Infrastructure/
Identity/
Network/
Screen/
Root/
Main/
Onboarding/
StudyHome/
StudyRecord/
MyPage/
Settings/
補助的に、まだ feature ディレクトリへ移し切っていない画面もあります。
Screen/QuizView.swiftScreen/ResultView.swiftScreen/ProfileView.swiftScreen/WrongAnswersView.swiftScreen/NotificationsView.swiftScreen/HelpAndSupportView.swiftScreen/LegacyTopView.swiftScreen/LegacyCategoryViews.swiftScreen/WorkbookListView.swiftScreen/WorkbookDetailView.swift
各レイヤの責務¶
Screen¶
SwiftUI の View を置く層です。
- 表示とユーザー操作の受け取りに集中する
- 初回読み込みは
taskなどから ViewModel を呼ぶ - API や Repository を直接触らない
feature ごとにディレクトリを切り、必要なら同階層に ViewModel を置きます。
例:
ViewModel¶
画面状態と画面単位の処理を持つ層です。
isLoadingerrorMessage- fetched data
- 画面表示向けの整形
- 初回 fetch
ViewModel は Observation の @Observable を使います。
例:
Domain/Entity¶
アプリの中心概念を置く層です。
QuestionWorkbookCategoryAnswerItem
View 都合でも API 都合でもなく、アプリで扱う意味のあるデータを置きます。
例:
Domain/UseCase¶
画面がやりたいことを明示する層です。
- 問題集一覧を取る
- 問題集詳細を取る
- カテゴリ一覧を取る
- 回答を送る
複数画面で共通化したい操作はここに置きます。
例:
Domain/Repository¶
データ取得の抽象です。
ViewModel / UseCase から見ると、どこからデータが来るかを隠します。
例:
Data/Remote¶
API や配信 JSON の request/response モデルを置く層です。
DTO ではなく、役割が分かるように Request / Response で分けています。
例:
- AnswerSubmissionRequest.swift
- WorkbookListResponse.swift
- CategoryListResponse.swift
- AnswerSubmissionResponse.swift
Data/Repository¶
Repository protocol の実装です。
現在は remote から取得する実装を置いています。
例:
Infrastructure¶
HTTP や device identity など、外部 I/O の詳細を置く層です。
例:
AppContainer¶
依存を束ねる composition root です。
例:
ここで Repository 実装を組み立てて UseCase を作ります。
AppState¶
画面横断の小さい shared state だけを持つ層です。
現在保持しているもの:
- オンボーディング完了状態
- ログイン状態
- 選択中の問題集 ID
- 学習結果の簡易サマリ
- 間違えた問題
例:
画面固有の fetched data や loading 状態は AppState に置かず、ViewModel に置きます。
Root からの流れ¶
現在の画面遷移の起点は RootView です。
flowchart TD
A[RootView] --> B{hasCompletedOnboarding?}
B -- No --> C[OnboardingView]
B -- Yes --> D[MainView]
D --> E[StudyHomeView]
詳細は navigation.md を参照してください。
モックが残っている場所¶
現状、問題系の取得は実 API / 実 JSON を使っていますが、周辺機能は仮実装が残っています。
実データ¶
- 問題集一覧
- 問題集詳細
- カテゴリ一覧
- カテゴリ詳細
- 回答送信 API
仮実装¶
- 学習記録の集計
- AppState.swift
- StudyRecordViewModel.swift
- マイページのショートカットやフッター
- MyPageViewModel.swift
- お知らせ一覧
- NotificationsView.swift
- FAQ / お問い合わせ
- HelpAndSupportView.swift
- Preview 用固定データ
- MockData.swift
運用ルール¶
- View から
UseCaseやRepositoryを直接呼ばない - 初回 fetch は ViewModel で行う
- 画面横断状態だけを
AppStateに置く - API request/response は
Data/Remote/RequestとData/Remote/Responseに置く - アプリの中心データは
Domain/Entityに置く - 新しい画面は
Screen/<Feature>/を作って、その中にViewとViewModelを置く
今後の整理候補¶
Screen/直下の画面を feature ディレクトリへ移すRemote/ResponseとEntityの mapper を明示的に分けるWrongAnswersやResultも ViewModel 経由へ寄せる- 学習記録やお知らせを仮実装から本実装へ置き換える