モバイル開発は、AIコーディングツールが実力を発揮できる場所でもあり、逆に目に見える形で失敗しやすい場所でもある。フレームワークは強い制約を持ち、ビルドツールチェーンは壊れやすく、コード生成の落とし穴はフレームワークごとに異なる。汎用的なCLAUDE.mdでは、Claude Codeが3タスクに1回は間違った方向に走ることになる。
このガイドでは、Flutter・React Native・Swift/SwiftUI・Kotlin/Composeという4つの主要なモバイルスタックそれぞれで、Claude Code・Cursor・OpenAI Codex CLI・Aiderがどう機能するかを整理する。各フレームワーク向けのCLAUDE.md / AGENTS.mdテンプレートはそのままコピーして使えるようにした。
Claude CodeでFlutterを使っていてそのスタックの深掘りを求めているなら、Claude Code Flutter完全ガイドでRiverpod・クリーンアーキテクチャ・テストを詳しく扱っている。この記事はその補完版として、スタックを切り替えたときに何が変わるか、各フレームワークでAIツールをどう設定するかにフォーカスする。
2026年のモバイルAIツール状況
AIコーディングツールは、「モバイル開発で使えるか?」という問いを超えた。今問うべきは「自分のスタックに合わせてどう設定すればうまく機能するか?」だ。
核心的な課題は、モバイルフレームワークがデフォルトではAIツールが知らない強い規約を持っていること:
- Flutter: Dartの型システム、Riverpodのリアクティブモデル、build_runnerによるコード生成
- React Native: JavaScript/TypeScriptブリッジ(現在はJSI/新アーキテクチャに移行中)、Metroバンドラーの癖、プラットフォーム固有のネイティブモジュール
- Swift/SwiftUI: Swiftの値セマンティクス、SwiftData/Core Dataのパターン、async/awaitの並行処理モデル、CombineとSwift Concurrencyの使い分け
- Kotlin/Compose: コルーチン、Flow、Composeの再コンポジション、Hilt依存性注入、Jetpackのパターン
明示的なルールがなければ、Claude Codeはコンパイルは通るがアーキテクチャを壊すコードを生成する。これはコンパイルエラーよりも質が悪い場合が多い。
ツール選択マトリクス
2026年現在、4つの主要ツールがモバイルスタックでどう機能するかを整理する:
| Claude Code | Cursor | Codex CLI | Aider | |
|---|---|---|---|---|
| Flutter | 優秀(Dart 3対応、コード生成に強い) | 良好(補完が強い) | まあまあ(build_runnerフックなし) | 良好(git-native、差分ベース) |
| React Native | 優秀(新アーキテクチャ対応) | 優秀(JS/TSエコシステムが強み) | 良好(npmスクリプト統合) | 良好 |
| ネイティブiOS (Swift) | とても良い(Swift 6並行処理対応) | 優秀(Xcodeコンテキスト) | まあまあ | まあまあ(Xcodeプロジェクトモデル非対応) |
| ネイティブAndroid (Kotlin) | とても良い(Compose再コンポジション対応) | とても良い | 良好 | 良好 |
どのツールをいつ使うか:
- Claude Code: アーキテクチャ作業、リファクタリング、CLAUDE.md / AGENTS.mdのルール作成、深いコンテキストが必要なタスク。大規模モバイルコードベースでは1Mコンテキストウィンドウが実際に効く。
- Cursor: オートコンプリートによる高速イテレーション、インライン編集、エディタ中心のワークフロー。
- Codex CLI: スクリプト化・再現可能なタスク(「このモデルのCRUD画面を生成」など)、CI/CDパイプライン統合。
- Aider: クリーンな差分としての変更が必要なgit-nativeワークフロー。コードレビュー準備に優秀。
経験豊富なモバイル開発者の多くは、Claude CodeとCursorを組み合わせて使う形に落ち着く。エディタはCursor、アーキテクチャ決定と大規模リファクタはClaude Codeという分担だ。
フレームワーク × ツール互換性マトリクス
フレームワーク別セクションの前に、何が機能して、何が設定を要し、何を避けるべきかをまとめる。
Flutter
| タスク | Claude Code | Cursor | Codex CLI | Aider |
|---|---|---|---|---|
| Widget生成 | うまく動く | うまく動く | 良好 | 良好 |
| Riverpodプロバイダ | ルール必要 | 補完設定が必要 | 苦手 | ルール必要 |
| build_runnerワークフロー | 明示的ルール必要 | 明示的ルール必要 | 苦手 | 明示的ルール必要 |
| クリーンアーキテクチャルーティング | ルールあれば動く | ルールあれば動く | まあまあ | うまく動く |
| プラットフォーム固有コード | ルール必要 | 良好 | まあまあ | 良好 |
React Native
| タスク | Claude Code | Cursor | Codex CLI | Aider |
|---|---|---|---|---|
| コンポーネント生成 | 優秀 | 優秀 | 良好 | 良好 |
| 新アーキテクチャ(JSI/TurboModules) | 良好 | とても良い | まあまあ | まあまあ |
| ナビゲーション(React Navigation) | うまく動く | うまく動く | 良好 | 良好 |
| ネイティブモジュールブリッジング | 詳細なルール必要 | 詳細なルール必要 | 苦手 | 苦手 |
| Expo vs. ベアワークフロー | ルール必要 | 良好 | 良好 | 良好 |
ネイティブiOS (Swift/SwiftUI)
| タスク | Claude Code | Cursor | Codex CLI | Aider |
|---|---|---|---|---|
| SwiftUIビュー構成 | とても良い | 優秀 | 良好 | 良好 |
| Swift 6並行処理 | ルール必要 | とても良い | まあまあ | まあまあ |
| SwiftData / Core Data | ルール必要 | 良好 | まあまあ | 苦手 |
| Package.swift依存関係 | 良好 | 良好 | 良好 | 良好 |
| CombineとSwift Concurrencyの使い分け | 明示的ルール必要 | ルール必要 | 苦手 | まあまあ |
ネイティブAndroid (Kotlin/Compose)
| タスク | Claude Code | Cursor | Codex CLI | Aider |
|---|---|---|---|---|
| Composable関数 | とても良い | 優秀 | 良好 | 良好 |
| Hilt DIの配線 | ルール必要 | とても良い | まあまあ | 良好 |
| ViewModel + Flowパターン | うまく動く | うまく動く | 良好 | 良好 |
| Roomデータベース | ルール必要 | 良好 | 良好 | 良好 |
| Gradle設定 | ルール必要 | 良好 | 良好 | 良好 |
フレームワーク別ワークフローレシピ
Flutter
FlutterはDartの型システムとコード生成の組み合わせにより、他のフレームワークよりも摩擦点が多いワークフローになる。良いニュースは、Claude Codeを正しく設定すれば、他では太刀打ちできないレベルでボイラープレートを処理できること。
FlutterとAIツールの核心的な問題:
Flutterプロジェクトはbuild_runnerを使ってアノテーションからコードを生成する(@freezed・@riverpod・@RestApi・@JsonSerializable)。AIツールはアノテーション付きファイルを変更した後にbuild_runnerを実行すべきことを知らず、.g.dart / .freezed.dartファイルが生成されたものであることも知らない。直接編集しようとしてしまう。
CLAUDE.mdでこれを明示的に扱う必要がある。以下は複数のAIツールを使うチーム向けに、Flutterガイドを拡張したルールだ:
## コード生成 — AIツールルール
### build_runnerが必要なもの
以下のアノテーションを持つファイルを変更した後、build_runnerが必ず必要:
- @freezed (Freezedモデル)
- @riverpod (Riverpodプロバイダ、ノティファイア)
- @RestApi (Retrofitクライアント)
- @JsonSerializable (JSONモデル)
- @TypedGoRoute (go_routerの型安全ルート)
### AIツールへのルール
- .g.dart や .freezed.dart ファイルを直接編集しない
- Provider()、StateNotifierProvider()、FutureProvider()を直接使うプロバイダを生成しない
— 常に@riverpodアノテーションパターンを使う
- アノテーション付きファイルへの変更後は必ずこのコマンドを出力:
dart run build_runner build --delete-conflicting-outputs
- .g.dartファイルがおかしければ、手動修正せず開発者にbuild_runner実行を伝える
### Cursor固有ルール
このプロジェクトでCursorと組み合わせる場合:
- カスタムコマンドを設定: Ctrl+Shift+B → dart run build_runner build --delete-conflicting-outputs
- パス指定で.g.dartファイルをインポートするオートコンプリート提案は受け入れない
(常にソースファイルからインポート)
Flutter + Claude Code のワークフロー(マルチセッションプロジェクト):
データフェッチを含む新しい画面を追加するタスク:
# タスク1: ドメイン層の生成
claude "ProductReviewのドメインエンティティとリポジトリインターフェースを追加。
features/users/domain/の既存Userエンティティパターンに従う。実装は不要。"
# タスク2: データ層の生成
claude "作成したインターフェースのProductReviewRepositoryImplを実装。
UserRepositoryImplのパターンを使う。リモートデータソースは今はモック。"
# タスク3: プロバイダの生成
claude "features/users/presentation/providers/の@riverpodアノテーションパターンに従い
ProductReview用Riverpodプロバイダを追加。リストプロバイダとuserIdパラメータパターンの
単一アイテムプロバイダの両方。"
# タスク3の後にbuild_runnerを実行:
dart run build_runner build --delete-conflicting-outputs
# タスク4: UIの生成
claude "ProductReviewListPageとProductReviewCardウィジェットを作成。
リストプロバイダにはAsyncValue.whenパターンを使う。UserListPageを参照として使う。"
このようにタスクを分割することで、各Claude Codeセッションを焦点を絞った状態に保ち、4つの層全体でコードを一度に生成しようとするのを防ぐ。同時に全部やろうとすると、build_runner出力が見えない状態で不整合が生まれやすい。
Cursor + Claude Code のハイブリッドワークフロー(Flutter):
Cursorのオートコンプリートはプロジェクト全体を見てパターンを学習するのでDartに強い。効果的な分担:
- Cursor: ウィジェット構成、ボイラープレートの補完、ファイル内リファクタリング
- Claude Code: アーキテクチャ決定、クロスファイルリファクタリング、新機能のエンドツーエンド生成、テスト作成
リスクはCursorの提案がCLAUDE.mdのルールを知らないこと。プロジェクトルートに.cursorrulesファイルを作成して重要なルールを反映させる:
# Flutterプロジェクトルール — Cursor
## 状態管理
- ONLY Riverpod with @riverpodアノテーション (riverpod_generator)
- setStateをfeature stateに使わない
- ChangeNotifier、ValueNotifier、GetX、Blocを提案しない
- ref.watch()はbuild()内、ref.read()はコールバック内のみ
## コード生成
- @freezed、@riverpod、@RestApi、@JsonSerializableのファイル変更後は
常に開発者に通知: dart run build_runner build --delete-conflicting-outputs
- .g.dart や .freezed.dart ファイルを直接編集しない
## ナビゲーション
- go_routerのみ使用 (context.go, context.push, context.pop)
- Navigator.of(context).pushは使わない
## ウィジェットパターン
- Riverpodが絡む場合はStatelessWidgetよりConsumerWidgetを優先
- 可能な限りconstコンストラクタ
- SafeAreaは全ページルートコンテンツをラップ
React Native
React NativeのAIツール事情はFlutterより複雑だ。エコシステムが分断されているため — Expo vs. ベアワークフロー、新アーキテクチャ vs. 旧アーキテクチャ、Bridgeベースのネイティブモジュールからのマイグレーション。
AIツールに必要なコンテキスト:
## React Nativeプロジェクト設定
### アーキテクチャ
- React Native 0.74+ で新アーキテクチャ有効(JSI、TurboModules、Fabric)
- Expo: [bare / managed] ワークフロー
(どちらか一方を削除)
- JavaScript/TypeScript: TypeScript strict mode
- ナビゲーション: React Navigation 7 (Stack, Tab, Drawer)
- 状態管理: [Zustand / Jotai / Redux Toolkit]
(どちらか一方を削除)
- 非同期: React Query(サーバー状態)+ Zustand(クライアント状態)
- スタイリング: StyleSheet(NativeWindを使う場合はTailwindアプローチ)
- ネイティブモジュール: Expo Modules API(旧NativeModulesブリッジではない)
### 新アーキテクチャルール
- 新機能にはNativeModulesブリッジを使わない — TurboModulesを使う
- NativeModules.* の旧呼び出しを使わない
- カスタムネイティブモジュールにはExpo Modules APIを使う
- NativeModules.*を見つけたらtech debtとしてフラグを立てる
### プラットフォーム固有コード
- Platform.OS === 'ios' と Platform.OS === 'android'(直接'ios'ではない)
- プラットフォーム固有ファイル: ComponentName.ios.tsx / ComponentName.android.tsx
- クロスプラットフォーム実装を優先し、必要な場合のみプラットフォーム分岐
- StyleSheet.create() — JSXにインラインスタイルを書かない
### Metroバンドラー
- 動的式でrequire()を使わない — Metroがツリーシェイクできない
- 循環インポートを避ける — 大規模ではMetroが苦手
- 画像アセット: 静的にインポート、動的パスを使わない
React Navigationのパターン — 落とし穴が多いエリア:
React Navigation 7はv6から型付けが大きく変わった。明示的なルールがなければ、Claude Codeはv7プロジェクトでもv6のパターンを生成することが多い:
## ナビゲーションルール (React Navigation 7)
### ルート型定義
// ナビゲーターレベルで型を定義:
type RootStackParamList = {
Home: undefined;
Profile: { userId: string };
Settings: undefined;
};
// navigationプロップを型付け:
type ProfileScreenNavigationProp = NativeStackNavigationProp<
RootStackParamList,
'Profile'
>;
### 使ってはいけないもの
- TypeScript型なしのnavigation.navigate('ScreenName')
- ジェネリック型パラメータなしのuseNavigation()
- タブナビゲーターでのnavigation.push()(jumpTo()を使う)
- router.back() — navigation.goBack()を使う
Expo固有のルール:
## Expo固有ルール
### EASビルド
- Expoコンフィグプラグインを使う(ネイティブモジュールファイルを直接変更しない)
- app.json / app.config.js — アプリ設定の唯一の正規情報源
- managedワークフローではandroid/やios/ディレクトリを手動編集しない
- 新しいネイティブ依存関係: npx expo install + EASビルドが必要
### 環境変数
- クライアント側変数はEXPO_PUBLIC_プレフィックス付き.envを使う
- サーバー専用シークレット: EAS Secrets(gitにコミットされた.envではない)
- アクセス: process.env.EXPO_PUBLIC_API_URL(process.env.API_URLではない)
ネイティブiOS(Swift / SwiftUI)
Swift/SwiftUIはAIツールのサポートが最も動的に変化している。Swift 6の厳格な並行処理チェック、新プロジェクトではCore DataをSwiftDataが置き換えていること、CombineからSwift Concurrencyへの成熟、これらすべてが影響する。古いSwiftコードベースで学習したAIツールは非推奨のパターンを提案する。
AIツールに必要なバージョンコンテキスト:
## iOSプロジェクト設定
### Swiftバージョンと並行処理
- Swift 6(厳格な並行処理チェック有効)
- Swift Concurrency(async/await、actors、structured concurrency)— 主要モデル
- Combine: 既存コードのみで使用。新しいCombineを導入しない
- 新しい非同期コードはすべてasync/awaitとactorsを使う
### データ永続化
- 新モデル: SwiftData (@Model, @Query, ModelContext)
- レガシーモデル: Core Data(既存のみ、新エンティティを追加しない)
- 新機能にCore Dataを使わない — SwiftDataを使う
### アーキテクチャ
- [MVVM / MV / TCA](どれか一つを残す)
- Observationフレームワーク(@Observable) — 新コードにObservableObject/@Publishedを使わない
- ナビゲーション: NavigationStack with NavigationPath
### 最小デプロイターゲット
- iOS 17.0(SwiftData、@Observable、NavigationStackが使える)
- iOS 17.0以降のAPIには@available(iOS X.0, *)チェックを必ず付ける
Swift 6並行処理 — AIツールが最も苦労するところ:
Swift 6の厳格な並行処理はAIツールにはなじみのないコンパイルエラーを生成する。最も一般的なミス:
## Swift 6並行処理ルール
### アクターの分離
- UIに関係する型はすべて@MainActor(ViewModel、ObservableObjectなど)
- UI以外のサービス: 共有可変状態にはactorを使う
- DispatchQueue.main.asyncを使わない — await MainActor.run { }または@MainActorをクラスにつける
### Sendable
- アクター境界を越えるとSendableが必要
- 値型(struct、enum)はデフォルトでSendable
- クラスは明示的なSendable適合または@unchecked Sendableが必要(なぜ安全か説明するコメント付き)
- コメントなしで並行処理警告を@unchecked Sendableで抑制しない
### 使ってはいけないもの(Swift 5のパターン)
- DispatchQueue.main.async { } → @MainActorまたはawait MainActor.runを使う
- 新しい非同期処理にコンプリーションハンドラ → async/awaitを使う
- 新ViewModelにObservableObject / @Published → @Observableを使う
SwiftDataのパターン:
## SwiftDataルール
### モデル定義
@Model
final class Task {
var title: String
var isCompleted: Bool
var createdAt: Date
init(title: String) {
self.title = title
self.isCompleted = false
self.createdAt = .now
}
}
// Core Dataパターンは新モデルに使わない:
// NSManagedObject、@NSManaged など
### クエリの使い方
// SwiftUIビューの中:
@Query(sort: \Task.createdAt, order: .reverse) private var tasks: [Task]
// 述語あり:
@Query(filter: #Predicate<Task> { !$0.isCompleted }) private var pendingTasks: [Task]
### ModelContextの操作
// 挿入:
modelContext.insert(Task(title: "新タスク"))
// 削除:
modelContext.delete(task)
// 保存は自動 — ほとんどの場合modelContext.save()を手動で呼ばない
// 例外: バックグラウンド操作の前
### 禁止
- Core DataとSwiftDataを同じモデルグラフに混在させる
- アクター境界を越えてmodelContextを渡す(明示的な受け渡しを除く)
- バックグラウンドコードでModelContainer.mainContextから直接フェッチ
XcodeのコンテキストとClaude Code:
Claude Codeはネイティブなxcodeプロジェクトファイルの認識がない。新ファイル追加時に.xcodeprojを更新できないため、CLAUDE.mdに以下を記載する:
## Xcodeプロジェクトルール
### ファイルの追加
新しいSwiftファイルを追加するとき:
1. 正しいグループディレクトリにファイルを作成
2. Xcodeのプロジェクトナビゲーターにドラッグするよう通知(またはXcodeの「Add Files」)
3. どのターゲットに属するか確認
Swift Package Manager (Package.swift) を使っている場合は、
Package.swiftの正しいターゲットに自動でファイルを追加する。
ネイティブAndroid(Kotlin / Compose)
Kotlin/Composeは新規Androidアプリ開発のデファクトスタンダードになったが、GradleとJetpackのパターンには複雑さが残っており、AIツールが頻繁に誤設定する。
設定コンテキスト:
## Androidプロジェクト設定
### 言語とバージョン
- Kotlin 2.x(K2コンパイラ)
- Jetpack Compose(UI層 — 新機能にXMLレイアウトを使わない)
- 最小SDK: 24 / ターゲットSDK: 35
- Compose BOM: [現在バージョン — libs.versions.tomlを確認]
### アーキテクチャ
- MVVM + MVI(単方向データフロー)
- ViewModel (Jetpack) + StateFlow + sealed class for UI state
- リポジトリパターン: ViewModel → Repository → DataSource
- 依存性注入: Hilt(Koinでも手動DIでもなく)
- ナビゲーション: Compose Navigation(Fragmentナビゲーションではない)
### 非同期
- コルーチン + Flow(主要モデル)
- 新コードにRxJavaを使わない
- ViewModelのコルーチンにはviewModelScope
- ActivityやFragmentにはlifeycleScope(稀 — ViewModelを優先)
### ビルドシステム
- バージョンカタログ付きGradle(libs.versions.toml)
- アノテーション処理にKSP(KAPTではない)
- Composeコンパイラプラグイン: [libs.versions.tomlのバージョン]
Composeの再コンポジション — AIツールが微妙なバグを作る場所:
Composeの宣言的モデルは、効率的なUIを書くために再コンポジションの理解が必要だ。Claude Codeはしばしば不必要な再コンポジションを引き起こすコードを生成する:
## Composeパフォーマンスルール
### 状態のホイスティング
- 状態はViewModelや最高位のComposableに持つ
- ステートレスなComposableは状態とコールバックを受け取る — 葉コンポーネントでViewModelを直接読まない
- パターン: Screen → ViewModel → UIState。ScreenがUIStateをコンポーネントに渡す
### 不要な再コンポジションを避ける
// Bad — コンポーネント内でViewModelを直接読む:
@Composable
fun UserName(viewModel: UserViewModel = hiltViewModel()) {
Text(text = viewModel.userName) // ViewModelの状態変化で何でも再コンポーズ
}
// Good — 必要なものだけ受け取る:
@Composable
fun UserName(name: String) {
Text(text = name) // nameが変わったときだけ再コンポーズ
}
### LazyColumnのkeys
// 安定したキーを必ず提供:
LazyColumn {
items(items = users, key = { it.id }) { user ->
UserItem(user = user)
}
}
### 派生状態
- 計算された状態にはremember { derivedStateOf { ... } }を使う — 再コンポーズ内で計算しない
- Composableスコープ内でコストの高い計算を直接行わない
Hilt依存性注入 — アノテーションの迷宮:
Hiltは全Androidライブラリの中で最もアノテーションが多く、AIツールが頻繁に混同する:
## Hiltルール
### コンポーネントアノテーション
- @HiltAndroidApp — Applicationクラス(プロジェクトに1つ)
- @AndroidEntryPoint — Activity、Fragment、View、Service(ViewModelではない)
- @HiltViewModel — ViewModel
- @Inject — コンストラクタインジェクション(フィールドインジェクションより優先)
- @Provides — @Moduleの中で、自分が所有しない型に
- @Binds — @Module @InstallInの中で、インターフェースのバインディングに
### 禁止事項
- ViewModelに@AndroidEntryPointを使わない — @HiltViewModelを使う
- ViewModelのフィールドインジェクションに@Injectを使わない — コンストラクタインジェクションを使う
- @InstallInなしで@Moduleを作らない
- @ViewModelScopedがより適切な場合に@ActivityScopedを使わない
ViewModel + Flowパターン — 2026年のAndroidの正規パターン:
## ViewModel + Flowパターン
### UI状態
sealed interface UserDetailUiState {
object Loading : UserDetailUiState
data class Success(val user: User) : UserDetailUiState
data class Error(val message: String) : UserDetailUiState
}
### ViewModelの構造
@HiltViewModel
class UserDetailViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,
private val userRepository: UserRepository
) : ViewModel() {
private val userId: String = checkNotNull(savedStateHandle["userId"])
val uiState: StateFlow<UserDetailUiState> = userRepository
.observeUser(userId)
.map { user -> UserDetailUiState.Success(user) }
.catch { e -> emit(UserDetailUiState.Error(e.message ?: "不明なエラー")) }
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = UserDetailUiState.Loading
)
}
// Composableの中での収集:
@Composable
fun UserDetailScreen(viewModel: UserDetailViewModel = hiltViewModel()) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
// ...
}
// collectAsState ではなく collectAsStateWithLifecycle を必ず使う —
// ライフサイクルを尊重し、バックグラウンド時に収集を停止する
フレームワーク別のCLAUDE.md / AGENTS.mdテンプレート
そのままコピーして使えるテンプレートをフレームワーク別に用意した。
Flutter — 最小CLAUDE.md
# Flutterプロジェクト — AIルール
## スタック
- Flutter 3.x / Dart 3.x / Null Safety
- 状態管理: Riverpod (riverpod_annotation, riverpod_generator)
- アーキテクチャ: Feature-firstクリーンアーキテクチャ
- ナビゲーション: go_router
- HTTP: Dio + Retrofit(コード生成)
- モデル: Freezed + json_serializable
- ビルド: build_runner
## 絶対に守るルール
1. 状態管理: @riverpodアノテーションパターンのみ。禁止: setState(featureの状態に)、ChangeNotifier、GetX、Bloc、Providerパッケージ
2. ナビゲーション: go_routerのみ。禁止: Navigator.of(context).push、MaterialPageRoute直接使用
3. @freezed、@riverpod、@RestApi、@JsonSerializable、@TypedGoRouteが付いたファイルへの変更後は常にbuild_runnerコマンドを出力
4. .g.dart や .freezed.dart を直接編集しない
5. ref.watchはbuild()内、ref.readはコールバック内 — 入れ替え禁止
## build_runnerコマンド
dart run build_runner build --delete-conflicting-outputs
## プロジェクト構造
lib/
├── core/ # 共有インフラ
├── features/ # feature-first(featureごとにdata/domain/presentation)
└── shared/ # 本当にクロスfeatureなウィジェット
React Native — 最小CLAUDE.md
# React Nativeプロジェクト — AIルール
## スタック
- React Native 0.74+ / 新アーキテクチャ(JSI有効)
- 言語: TypeScript (strict: true)
- ナビゲーション: React Navigation 7
- 状態管理: Zustand(クライアント)+ React Query(サーバー)
- スタイリング: StyleSheet(インラインスタイル禁止)
- ネイティブモジュール: Expo Modules API
- [Expo managed / Expo bare / Bare RN]
## 絶対に守るルール
1. 新機能にNativeModulesブリッジを使わない — TurboModulesまたはExpo Modules APIを使う
2. すべてのコンポーネントをTypeScriptで明示的なprop型付き(anyは禁止)
3. TypeScriptのparamList型付きナビゲーション — 型なしのnavigate()は禁止
4. プラットフォーム固有コード: Platform.OSチェックまたは.ios.tsx / .android.tsxファイル
5. インラインスタイル禁止 — StyleSheet.create()を常に使う
6. 動的なrequire()禁止 — Metroが最適化できない
## 禁止パターン
- NativeModules.*(TurboModulesを使う)
- React.FCジェネリック(明示的な関数シグネチャを使う)
- サーバーデータにuseState(React Queryを使う)
- データフェッチにuseEffect(React Queryを使う)
ネイティブiOS — 最小CLAUDE.md
# iOSプロジェクト — AIルール
## スタック
- Swift 6 / SwiftUI
- 最小デプロイ: iOS 17.0
- アーキテクチャ: @Observableを使ったMVVM
- データ: SwiftData
- 非同期: Swift Concurrency (async/await, actors)
- 新コードにCombineを使わない
- 新機能にUIKitを使わない
## 絶対に守るルール
1. Swift 6の厳格な並行処理 — SendableとアクターアイソレーションエラーをすべてFixし、抑制しない
2. すべてのViewModelとUI更新コードに@MainActor
3. 新しい永続化: SwiftDataのみ。新規Core Dataエンティティを追加しない
4. 新しいリアクティブコード: @Observable。新規ObservableObject / @Publishedを追加しない
5. 新しい非同期コード: async/await。新規コンプリーションハンドラとDispatchQueue.main.asyncを使わない
6. NavigationStackと型付きNavigationPath — NavigationLink(destination:)を使わない
## ファイルの追加
新しい.swiftファイルはXcodeプロジェクトナビゲーターに手動で追加する必要がある旨を通知する。
Claude Codeは.xcodeproj を直接更新できない。
ネイティブAndroid — 最小CLAUDE.md
# Androidプロジェクト — AIルール
## スタック
- Kotlin 2.x / K2コンパイラ
- Compose UI(新機能にXMLレイアウトなし)
- アーキテクチャ: MVVM + 単方向データフロー
- DI: Hilt
- 非同期: コルーチン + Flow(RxJavaなし)
- ナビゲーション: Compose Navigation
- データベース: Room(KSP使用、KAPTなし)
- HTTP: Retrofit + OkHttp
## 絶対に守るルール
1. すべてのDIにHilt — 手動インジェクションもKoinも不可
2. アノテーション処理にKSP(Room、Hilt)— KAPTは絶対に使わない
3. UI状態はsealed interface/class — Booleanフラグの羅列は禁止
4. ComposableでcollectAsStateWithLifecycle(collectAsStateではない)
5. ViewModel → UIにはStateFlow、ワンショットイベントにはSharedFlow
6. 新コードにRxJavaを使わない
7. Composable関数: ステートレスなコンポーネントは状態とコールバックのみを受け取る
## Gradle
- バージョンカタログ: libs.versions.toml(バージョンの唯一の情報源)
- build.gradle.ktsファイルに依存バージョンをハードコードしない
- KSPプロセッサ、KAPTではない
マルチフレームワーク AGENTS.md
複数ターゲットまたはフレームワークを含むコードベースでAIエージェントタスクを実行する場合:
# AGENTS.md
## プロジェクト構造
このリポジトリには以下が含まれる:
- /mobile/flutter/ — FlutterモバイルアプリSS
- /mobile/rn/ — React Nativeウェブコンパニオン
- /backend/ — Node.js API
## エージェントのスコープルール
- モバイルタスクは分離: /mobile/flutter/への変更が/mobile/rn/に触れてはいけない
- タスクに明示的なAPI変更が含まれない限り/backend/を変更しない
- クロスプラットフォームの変更(共有型、APIコントラクト): /shared/のみを変更し、
各プラットフォームに個別に適用
## タスクの分解
複数プラットフォームにまたがるタスクは以下に分解:
1. 共有コントラクトの変更(/shared/)
2. Flutter実装
3. React Native実装
各ステップは独立してテスト可能であること。
## プラットフォーム別ビルドコマンド
- Flutter: cd mobile/flutter && flutter build apk --debug
- React Native: cd mobile/rn && npx react-native run-android
- Backend: cd backend && npm test
- 全体: Makefileターゲットを使う(make test-all, make build-all)
## 禁止
- 間違ったディレクトリからプラットフォーム固有のビルドコマンドを実行
- プラットフォーム間でインポートを混在させる
- FlutterタスクからpackkageJsonを変更、RNタスクからpubspec.yamlを変更
参考実装(実在するオープンソースアプリ)
以下のオープンソースアプリは上記のパターンを実際に示している。新しいCLAUDE.mdルールを設定する前に「正しい実装」をClaude Codeに見せるのに役立つ。
Flutter
flutter_shopping_app(VGV) Very Good Venturesのサンプルアプリ。クリーンアーキテクチャ、Riverpod、go_router。プロダクショングレードのパターン。新プロジェクトでCLAUDE.mdルールを設定する際の良いリファレンス。
holobooth Flutterチームの公式サンプル。カメラ/AR統合パターンとプラットフォームチャネルを必要とするプラットフォーム固有コードを示している。
React Native
Rainbow Ethereumウォレットアプリ。複雑なナビゲーション、ネイティブモジュール統合(生体認証、カメラ)、TypeScriptの規律の良い例。重要なネイティブコードを持つアプリのCLAUDE.mdルール構築の参考に。
ネイティブiOS
TCA サンプル TCAを使っているならこのサンプルが正規パターンを示している。TCA固有の規約(Reducer、Store、ViewStore)に関するルール設定に有用。
Scrumdinger AppleのSwiftUIチュートリアルアプリ。SwiftDataとSwift Concurrencyを使用。プロダクション規模ではないが、iOS 17の正しいパターンを示している。
ネイティブAndroid
Now in Android Googleのリファレンスアプリ。Hilt、Compose、KSP、バージョンカタログ。AndroidのCLAUDE.mdルール設定の参考として最良。
Jetpack Compose Samples JetSnack、Owl、Rally — それぞれ異なるCompose UIパターンを示している。CLAUDE.mdに「このパターンに従う」として貼る具体的な例の良いソース。
落とし穴とFAQ
なぜClaude Codeは間違った状態管理パターンを生成し続けるのか?
状態管理はどのモバイルフレームワークにも複数の有効なアプローチがあり、明示的な禁止なしではClaude Codeはその場に合うパターンを使う。修正方法は常に明示的なルール:
## 状態管理ポリシー
このプロジェクトはONLY [Riverpod / Zustand + React Query / @Observable / StateFlow]を使用。
絶対に導入しないこと: [禁止パターンのリスト]。
例外が必要と思ったら実装前に確認すること。
禁止リストはポジティブなルールと同じくらい重要だ。
CursorがCLAUDE.mdのルールを守らないのはなぜ?
CursorはデフォルトでCLAUDE.mdを読まない。必要なのは:
- プロジェクトルートの
.cursorrulesファイル(Cursorが自動で読む) - Cursor設定パネルでプロジェクトコンテキストにルールを追加
.cursorrulesファイルはCLAUDE.mdから最重要なルールを反映させる。特に状態管理、ナビゲーション、コード生成のルール。全部を複製する必要はない。
旧アーキテクチャから新アーキテクチャへの移行中のプロジェクトはどう扱うか?
移行はAIツールに最も難しいシナリオの一つだ。コードベースに両方のパターンが混在しているため:
## 移行コンテキスト
現在[旧パターン]から[新パターン]へ積極的に移行中。
### 新しいコードのルール(厳格)
全ての新規ファイル: [新パターンのルール]
### レガシーコードのルール
[レガシーパス]内の既存ファイルに触る場合は、差分サイズを最小化するために
既存パターンを使ってよい。タスクが明示的に求めない限りレガシーコードをリファクタしない。
### 移行済み
- 完了: [移行済みモジュールのリスト]
- 進行中: [現在のモジュール]
- 未着手: [残りのモジュール]
### 禁止
- 単一ファイル内でパターンを混在させる
- 近くのファイルが使っていても新規ファイルに旧パターンを導入する
Swift 6並行処理がAIの提案を壊し続けるのはなぜか?
Swift 6の厳格な並行処理チェックはコンパイル時にデータレースを検出する。主な問題:
@MainActorの代わりにDispatchQueue.main.async- アクター境界を越えてnon-Sendable型を渡す
- 適切なTaskラッピングなしで同期コンテキストから
async関数を呼ぶ - 複数のコンテキストから共有状態を変更する
解決策: このガイドのSwift 6並行処理ルールのセクションをCLAUDE.mdに含め、特定のルールを追加する。「すべての並行処理警告はエラー。なぜ安全かを説明するコメントなしに@unchecked Sendableやnonisolated(unsafe)で抑制しない」。
モバイルのCLAUDE.mdの適切な詳細度は?
過剰な仕様化には実コストがある。非常に長いCLAUDE.mdファイルはClaude Codeを遅くし、パターンが進化した時のルール更新を難しくする。
実用的なガイドライン:
- アーキテクチャと状態管理: 常に明示的に指定
- 命名規約: 非自明なものを指定(Dartのsnake_caseは自明、
_page.dartサフィックスは非自明) - コード生成ワークフロー: 常に指定(build_runner、KSP、Hiltプロセッサ)
- 禁止パターン: 常に明示的にリスト — ポジティブなルールだけでは不十分
- ウィジェット/Composableの分解しきい値: チームで議論になるなら指定
- テストのレベル: どのタイプのテストがどこに属するかを指定
上記の最小テンプレートから始めて、Claude Codeが特定の間違いをした時にルールを追加し、一度もトリガーされないルールは削除していく。
このガイドのフレームワーク固有のパターンはツールのバージョン自体よりも緩やかに変化する。特にReact Nativeの新アーキテクチャが安定したとき、Composeマルチプラットフォームが成熟したとき、Swift 6の採用が普遍的になったときには、ツール互換性マトリクスのセクションを見直す価値がある。CLAUDE.mdテンプレートは安定しているが、ツールの推奨事項は2026年時点のスナップショットだ。
フレームワーク固有のルールを実際のプロジェクトから集めたギャラリーや、Dart/Riverpodスタックの深掘りはFlutter完全ガイドで。