AIエージェントを活用したiOSアプリ開発:実践者ガイド
# AIエージェントを活用したiOSアプリ開発
要約: 3つのエージェントランタイムがiOS向けコードを生成できるようになりました。Claude Code CLI(MCP対応)、Codex CLI(MCP対応)、そしてXcode 26.3のネイティブIntelligenceエージェントです。2つのMCPサーバー(59ツールを備えたXcodeBuildMCPと、20ツールを備えたAppleの
xcrun mcpbridge)により、エージェントはビルド、テスト、シミュレーター、デバッグへの構造化されたアクセスが可能になります。本ガイドでは、実際のCLAUDE.mdパターン、フック設定、そして何がうまくいき何がうまくいかないかの率直な評価を扱います。これらは293のSwiftファイルからなる8つの本番iOSアプリでの経験に基づいています。エージェントはSwiftUIビュー、SwiftDataモデル、リファクタリング、ビルドエラー診断に優れています。一方で、.pbxprojの変更、コード署名、ビジュアルデバッグには失敗します。「エージェントがSwiftを書く」と「エージェントがiOSアプリを出荷する」の間のギャップを埋めるのは、プロンプトではなく設定です。
私は8つのiOSアプリをAIコーディングエージェントで開発しています。プロトタイプではありません。HealthKit連携、Metalシェーダー、SpriteKit物理演算、iCloud同期、Live Activities、Game Centerリーダーボード、そしてiOS・watchOS・tvOSにまたがるマルチプラットフォームターゲットを備えた、App Store公開済みのアプリです。これらのアプリのSwiftコードはすべて、エージェントが書いて私がレビューしたか、私が書いてエージェントがリファクタリングしたかのどちらかです。その比率はおよそ85対15で、エージェント側が優勢と見積もっています。
本ガイドは、私が始めた当時に存在してほしかった完全リファレンスです。使用するエージェントランタイムの選択、構造化ビルドアクセスのためのMCPサーバー設定、CLAUDE.mdに記載すべき内容、エージェントによるXcodeプロジェクト破壊を防ぐフック、そして最も重要な点として、エージェントが失敗し人間の介入が必要になる場面まで、フルスタックを網羅しています。
重要なポイント
AIエージェント初心者のiOS開発者向け:
- Claude Code CLI + XcodeBuildMCPから始めましょう。 最も成熟したランタイムであり、MCPツールカバレッジが最も充実しています。2つのコマンドをインストールし、プロジェクトにCLAUDE.mdを追加すれば、エラーメッセージをコピーしなくても、エージェントがビルド、テスト、デバッグを実行できます。
- エージェントに.pbxprojを変更させてはいけません。 これが最も重要なルールです。
.pbxprojと.xcodeproj/への書き込みをブロックするPreToolUseフックを設定すれば、何時間ものリカバリー作業を回避できます。 - CLAUDE.mdはエージェントのオンボーディングドキュメントです。 CLAUDE.mdに費やす1時間は、エージェントのミスを修正する10時間を節約します。
iOS開発をワークフローに追加する経験豊富なエージェントユーザー向け:
- MCPがiOSビルドループを変革します。 MCP導入前、エージェントはSwiftを書けてもコンパイルの検証ができませんでした。XcodeBuildMCPがあれば、エージェントはコードの記述、ビルド、構造化されたエラーの読み取り、修正、テスト実行を自律的に行えます。
- 3つのランタイムはそれぞれ異なるニーズに対応します。 Claude Code CLIは深いエージェントセッションに、Codex CLIはヘッドレスなバッチ処理に、Xcode 26.3ネイティブエージェントはIDEを離れずに素早くインライン修正を行う場合に適しています。
- フックインフラはそのまま転用できます。 既存のPostToolUseフォーマッター、PreToolUseブロッカー、テストランナーフックは、パスの微調整だけでiOSプロジェクトでも同様に機能します。
AI支援iOS開発を評価するチームリーダー向け:
- エージェントの効果はプロジェクトの規模ではなく、ドキュメントの充実度に比例します。 詳細なCLAUDE.mdを持つ63ファイルのアプリは、ドキュメントのない14ファイルのアプリよりも優れたエージェント出力を生み出します。
- .pbxprojの境界線は譲れません。 エージェントはXcodeプロジェクトファイルを確実に編集できません。ワークフローにはXcodeターゲットへの手動ファイル追加を組み込む必要があります。
- 率直なROI:エージェントは実装作業の70〜80%を処理できます。 残りの20〜30%、つまりビジュアルポリッシュ、署名、パフォーマンスチューニング、App Store申請には人間の判断が必要です。
進め方の選択
| 目的 | 参照先 |
|---|---|
| 初めてMCPをセットアップする | MCPセットアップ:完全設定ガイド — 両サーバーのインストール、検証、エージェント設定 |
| iOSプロジェクト用のCLAUDE.mdを書く | iOS向けCLAUDE.mdパターン — 8つのアプリからの実例 |
| 3つのエージェントランタイムを比較する | iOS向け3つのエージェントランタイム — Claude Code vs. Codex vs. Xcodeネイティブ |
| エージェントの能力と限界を理解する | エージェントが得意なこととエージェントが苦手なこと |
| iOS開発用フックをセットアップする | iOS開発用フック — 保存時フォーマット、.pbxproj保護、テストランナー |
| 詳細リファレンス(このページ) | このまま読み進めてください — セットアップから高度なパターンまですべてを網羅 |
本ガイドの使い方
本ガイドは3,000行以上のリファレンスです。ご自身の経験レベルに合った箇所からお読みください:
| 経験 | 開始ポイント | 次のステップ |
|---|---|---|
| iOS・エージェントとも初心者 | 前提条件 → MCPセットアップ → 初めてのエージェントセッション | CLAUDE.mdパターン、得意・不得意な領域 |
| iOS開発者、エージェント初心者 | 3つのランタイム → MCPセットアップ → CLAUDE.md | フック、アーキテクチャパターン |
| エージェントユーザー、iOS初心者 | アーキテクチャパターン → エージェントが苦手なこと → CLAUDE.md | フレームワーク固有のコンテキスト、高度なワークフロー |
| 両方の経験者 | 高度なワークフロー → フック → マルチプラットフォームパターン | ランタイム比較マトリクス、ポートフォリオ:8アプリ、293ファイル |
目次
- ポートフォリオ:8アプリ、293ファイル
- 前提条件
- iOS向け3つのエージェントランタイム
- MCPセットアップ:完全設定ガイド
- iOS向けCLAUDE.mdパターン
- 初めてのエージェントセッション
- iOSでエージェントが得意なこと
- iOSでエージェントが苦手なこと
- iOS開発用フック
- エージェントと相性の良いアーキテクチャパターン
- フレームワーク固有のコンテキスト
- マルチプラットフォームパターン
- 高度なワークフロー
- 実際のケーススタディ
- エージェントを活用したプロジェクトライフサイクル
- エージェント定義の設定
- エージェント支援iOSのテストパターン
- iOSプロジェクトのコンテキストウィンドウ管理
- トラブルシューティング
- iOSでのよくあるエージェントミスと防止策
- 率直な評価
- FAQ
- クイックリファレンスカード
- 参考資料
関連リソース
| トピック | リソース |
|---|---|
| Xcode向けMCPセットアップ(短いブログ記事) | 2つのMCPサーバーでClaude CodeがiOSビルドシステムに |
| Claude Code CLI完全リファレンス | Claude Code CLI:完全ガイド |
| Codex CLIリファレンス | Codex CLI:完全ガイド |
| フックシステム詳解 | Anatomy of a Claw: 84 Hooks as an Orchestration Layer |
| エージェントアーキテクチャパターン | エージェントアーキテクチャガイド |
ポートフォリオ:8つのアプリ、293ファイル
設定の詳細に入る前に、このガイドの基盤となったプロジェクト群を紹介します。これらはサンプルアプリではありません。5つのAppleフレームワーク、3つのプラットフォームにまたがり、14ファイルのワークアウトトラッカーから63ファイルのマルチプラットフォーム瞑想タイマーまで、iOSの複雑さの全範囲を網羅しています。
| アプリ | スタック | ファイル数 | 複雑さ |
|---|---|---|---|
| Banana List | SwiftUI + SwiftData + iCloud Driveの同期 + Claude Desktop用MCPサーバー | 53 | フルCRUD、iCloud同期、アプリのデータをClaude Desktopに公開するカスタムMCPサーバー |
| Ace Citizenship | SwiftUI学習アプリ + FastAPIバックエンド | 26 | クライアント・サーバー構成、REST API連携、クイズエンジン |
| TappyColor | SpriteKitカラーマッチングゲーム | 30 | ゲームループ、物理演算、タッチ操作、パーティクルエフェクト |
| Return | Zen瞑想タイマー — iOS 26+、watchOS、tvOS | 63 | HealthKit、Live Activities、Watch拡張ランタイム、TVフォーカスナビゲーション、iCloudセッション同期 |
| amp97 | Metalシェーダー + オーディオビジュアライゼーション | 41 | カスタムMetalレンダーパイプライン、音声解析、リアルタイムGPUコンピュート |
| Reps | SwiftUI + SwiftDataワークアウト記録 | 14 | 最小限の実用アプリ、クリーンなSwiftDataパターン |
| Water | SwiftUI + SwiftData + Metal + HealthKit水分補給記録 | 34 | Metal流体シミュレーション、HealthKit水分摂取ログ、ウィジェット |
| Starfield Destroyer | SpriteKit + Metalスペースシューター | 32 | 99レベル、8機の宇宙船、Game Centerリーダーボード、Metalポストプロセッシング |
ファイル数が重要な理由: エージェントの効果はプロジェクトの規模ではなく、プロジェクトの可読性に比例します。Return(63ファイル)はamp97(41ファイル)よりも優れたエージェント出力を生成しますが、これはReturnにファイルの注釈、アーキテクチャ図、明示的なパターンを含む詳細なCLAUDE.mdがあるためです。amp97のMetalシェーダーは、ドキュメントの品質に関係なく、エージェントが推論するのが本質的に困難です。
前提条件
iOS開発でエージェントランタイムをセットアップする前に、以下を確認してください。
必須: - macOS 15+(Sequoia)またはmacOS Tahoe - Xcode 26.3+がインストール・設定済み(ライセンス同意済み、プラットフォームダウンロード済み) - iOSシミュレータランタイムが少なくとも1つインストール済み - Anthropic APIアカウント(Claude Code用)またはOpenAIアカウント(Codex用)
推奨:
- SwiftFormatのインストール(brew install swiftformat)— 保存時フォーマットフックで使用
- SwiftLintのインストール(brew install swiftlint)— スタイル適用に有用(任意)
- ターミナル操作への習熟 — 3つのランタイムすべてがコマンドラインから操作またはコマンドラインと連携
Xcodeインストールの確認:
# Check Xcode version
xcodebuild -version
# Expected: Xcode 26.3 or later
# Check available simulators
xcrun simctl list devices available
# Expected: at least one iPhone simulator
# Verify xcrun mcpbridge is available
xcrun mcpbridge --help
# Expected: usage information (not "command not found")
xcrun mcpbridgeが「command not found」を返す場合、Xcode 26.3以降が必要です。App Storeまたはdeveloper.apple.comからXcodeをインストールまたはアップデートしてください。注意:xcode-select --installはCommand Line Toolsのみをインストールするため、mcpbridgeは含まれません。完全なXcode.appが必要です。
iOS向け3つのエージェントランタイム
3つの異なるランタイムが、iOSコードの記述、ビルド、テストを行えます。これらは互換性がなく、それぞれ異なる強み、異なるMCP統合パターン、異なる最適なユースケースを持っています。
1. Claude Code CLI
概要: Anthropicのターミナルベースのエージェントコーディングアシスタントです。コードベースの読み取り、コマンドの実行、ファイルの変更、MCPを介した外部ツールとの連携が可能です。
MCP統合: XcodeBuildMCPとAppleのXcode MCPの両方を完全サポートしています。エージェントはMCPプロトコルでツールを検出し、構造化されたパラメータで呼び出します。両サーバー合わせて59 + 20のツールが利用可能です。
セットアップ:
# Install Claude Code (if not already installed)
claude --version # verify installation
# Add XcodeBuildMCP (59 tools — builds, tests, simulators, debugging)
claude mcp add XcodeBuildMCP \
-s user \
-e XCODEBUILDMCP_SENTRY_DISABLED=true \
-- npx -y xcodebuildmcp@latest mcp
# Add Apple Xcode MCP (20 tools — file ops, diagnostics, Swift REPL, previews)
claude mcp add --transport stdio xcode \
-s user -- xcrun mcpbridge
最適な用途: 本格的な実装セッション — 新機能の構築、複数ファイルにわたるリファクタリング、複雑な問題のデバッグ、ビルド・テスト・修正ループの自律実行に適しています。Claude Codeの1Mトークンコンテキストウィンドウ(Opus 4.6)により、小〜中規模のiOSプロジェクトのほとんどをワーキングメモリに保持できます。私の経験では、ファイルサイズにもよりますが、おおよそ50ファイルまで対応可能です。
典型的なセッション:
You: Add a new SettingsView with toggles for notifications and haptic feedback.
Use the existing Settings.swift pattern from TimerManager.
Build for simulator after implementation and fix any errors.
Claude: [reads Settings.swift, TimerManager.swift, existing views]
[creates SettingsView.swift]
[calls build_sim via MCP]
[reads structured error: missing import HealthKit]
[fixes import]
[calls build_sim again — success, 0 errors, 1 warning]
[calls test_sim — all tests pass]
MCP以前のワークフローとの決定的な違いは、エージェントが手動ビルドやエラー出力の貼り付けを要求しないことです。ビルド・エラー・修正のループは自律的に進行します。
2. Codex CLI
概要: OpenAIのターミナルベースのコーディングエージェントです。コンセプトはClaude Codeと類似していますが、OpenAIモデル(GPT-4o、o3)を使用し、権限モデルが異なります。
MCP統合: Codexはcodex mcp addコマンドでMCPをサポートしています。AppleのXcode MCPはそのまま動作します:
# Add Apple Xcode MCP to Codex
codex mcp add xcode -- xcrun mcpbridge
XcodeBuildMCPも同じnpxコマンドでCodexに対応しています:
# Add XcodeBuildMCP to Codex
codex mcp add XcodeBuildMCP -- npx -y xcodebuildmcp@latest mcp
最適な用途: ヘッドレスバッチ処理、CI/CD統合、および異なるモデルファミリーからセカンドオピニオンを得たい場面に適しています。Codexのサンドボックスモードは隔離環境でコードを実行するため、状態を変更するテストスイートの実行など、破壊的な操作に有用です。
Claude Codeとの主な違い:
- ClaudeモデルではなくOpenAIモデルを使用
- コンテキストウィンドウサイズとトークンコストが異なる
- サンドボックス優先の権限モデル(デフォルトでより制限的)
- MCPエコシステムが小さい(テスト済みのコミュニティサーバーが少ない)
- フックシステムが利用可能(v0.119.0+)だが、Claude Codeより成熟度が低い — イベントタイプが少なく、条件分岐のifフィールドがない
iOS開発でCodexをClaude Codeより優先する場面:
モデルの多様性が求められる場面で使用しましょう。1つ目のエージェントが書いたコードを2つ目のエージェントがレビューすることで、異なるクラスのエラーを検出できます。collabワークフロー(Claudeがビルド、Codexがレビュー)はiOS開発で効果的です。一方のモデルファミリーでは正しく見えるSwiftUIパターンにも、もう一方では微妙な問題が見つかることがあるためです。Metalシェーダーや並行処理パターンは、デュアルモデルレビューの恩恵を特に受けます。
3. Xcode 26.3ネイティブエージェント
概要: AppleはAIコーディングエージェントをXcodeのIntelligenceパネルに直接統合しました。Xcode 26.3では、Xcode設定 > IntelligenceでClaude AgentとCodexをインテリジェンスプロバイダーとして設定できます。
セットアップ:
- Xcode 26.3+を開く
- Settings > Intelligenceに移動
- 新しいプロバイダーを追加:
- Claudeの場合:「Claude Agent」を選択し、Anthropic APIキーを入力
- Codexの場合:「Codex」を選択し、OpenAI APIキーを入力
- Intelligenceサイドバーにエージェントが表示され、インラインで呼び出し可能
最適な用途: 素早いインライン編集、エージェントレベルの推論を伴うコード補完、Xcodeから離れたくない開発者に適しています。ネイティブ統合により、エージェントはXcodeのプロジェクトコンテキスト — 開いているファイル、ビルドターゲット、スキーム設定 — にMCPブリッジなしで直接アクセスできます。
CLIエージェントとの比較での制限事項: - フックシステムなし — 保存時フォーマットや.pbxproj書き込みのブロックを強制できない - CLAUDE.md読み込みなし — プロジェクトレベルの設定ファイルを読み取らない - 自律性が限定的 — プロジェクト全体ではなく、現在のファイルや選択範囲で動作 - サブエージェント委任なし — 複雑なマルチステップタスクの並列実行が不可 - MCPサーバー設定なし — Xcode組み込みツールのみ使用
Xcodeネイティブエージェントの使用場面:
ターミナルへの切り替えがオーバーヘッドとなる、素早くスコープが限定された編集に適しています。「このモデルに算出プロパティを追加して」「この関数のユニットテストを書いて」「このビューを@Observableを使うようリファクタリングして」など、1〜2ファイルに限定され、ビルド・テストサイクルを必要としないタスクに向いています。
ビルド、テスト、複数ファイルのリファクタリング、自律的なエラー修正が必要な場合は、MCPを備えたCLIエージェントを使用してください。
ランタイム比較マトリックス
| 機能 | Claude Code CLI | Codex CLI | Xcode 26.3ネイティブ |
|---|---|---|---|
| MCPサポート | フル(79ツール) | フル(79ツール) | Xcode組み込みツールのみ |
| フックシステム | あり(成熟) | あり(基本的、v0.119.0+) | なし |
| CLAUDE.md / プロジェクト設定 | あり | codex.md相当 | なし |
| 自律ビルド・テスト・修正 | あり(MCP経由) | あり(MCP経由) | 部分的(インラインのみ) |
| サブエージェント委任 | あり(最大10並列) | なし | なし |
| コンテキストウィンドウ | 1Mトークン(Opus 4.6) | モデルにより異なる | プロバイダーにより異なる |
| 複数ファイル操作 | コードベース全体にアクセス可能 | コードベース全体にアクセス可能 | 現在のファイル / 選択範囲 |
| .pbxproj保護 | フック経由 | 手動 | 該当なし(Xcodeネイティブ使用) |
| 保存時フォーマット | PostToolUseフック経由 | 外部ツール | Xcode設定 |
| オフライン機能 | なし | なし | なし |
| コストモデル | Anthropic API使用量 | OpenAI API使用量 | プロバイダーAPI使用量 |
推奨構成: メインランタイムとしてClaude Code CLIを使用しましょう。素早いインライン編集にはXcode 26.3ネイティブエージェント、レビューパスやバッチ処理にはCodex CLIを使用してください。3つは競合ではなく補完の関係にあります。
MCPセットアップ:完全な設定ガイド
MCP(Model Context Protocol)は、エージェントを「Swiftを書いてビルドを祈るだけ」の存在から「Swiftを書き、ビルドし、構造化されたエラーを読み取り、自ら修正する」存在へと変革するものです。このセクションではブログ記事よりも踏み込んだ内容を扱います — 両方のサーバー、すべてのインストール方法、検証手順、そしてツールが確実に使用されるためのエージェント設定までカバーしています。
XcodeBuildMCP:ヘッドレスiOS開発のための59ツール
XcodeBuildMCPは、xcodebuild、xcrun simctl、LLDBを59の構造化されたMCPツールとしてラップしています。Xcodeを起動せずに動作し、ビルド・テスト・デバッグのサイクル全体がAppleのコマンドラインツールを通じてヘッドレスで実行されます。
インストール方法:
# Option 1: Via npx (recommended — always uses latest version)
claude mcp add XcodeBuildMCP \
-s user \
-e XCODEBUILDMCP_SENTRY_DISABLED=true \
-- npx -y xcodebuildmcp@latest mcp
# Option 2: Via Homebrew (pinned version, manual updates)
brew install xcodebuildmcp
claude mcp add XcodeBuildMCP \
-s user \
-e XCODEBUILDMCP_SENTRY_DISABLED=true \
-- xcodebuildmcp mcp
# Option 3: Project-scoped (omit -s user)
claude mcp add XcodeBuildMCP \
-e XCODEBUILDMCP_SENTRY_DISABLED=true \
-- npx -y xcodebuildmcp@latest mcp
-s userフラグを指定すると、サーバーがすべてのプロジェクトでグローバルに利用可能になります。省略するとプロジェクトスコープでのインストールとなり、iOS以外のプロジェクト(Webプロジェクトなど)ではMCPを使いたくない場合に便利です。
-e XCODEBUILDMCP_SENTRY_DISABLED=true環境変数は、クラッシュレポートのテレメトリを無効にします。XcodeBuildMCPにはデフォルトでSentryが含まれており、ファイルパスを含むエラーデータが送信されます。プロジェクトへの診断データ提供を希望しない限り、オプトアウトしてください。1
ツール一覧(8カテゴリにわたる59ツール):
| カテゴリ | ツール | 機能 |
|---|---|---|
| プロジェクト検出 | discover_projs、list_schemes、list_targets |
.xcodeproj/.xcworkspaceファイルの検索、利用可能なスキームとターゲットの一覧表示 |
| ビルド | build_sim、build_device、build_mac |
ファイルと行番号ごとに構造化されたJSONエラー・警告出力付きでビルド |
| テスト | test_sim、test_device |
メソッド単位の合否結果付きでテストを実行 |
| シミュレータ管理 | list_sims、boot_sim、shutdown_sim、open_sim、session_set_defaults |
シミュレータの作成、起動、管理、設定 |
| デバイス管理 | list_devices、install_device、launch_device |
実機へのデプロイと管理 |
| デバッグ | debug_attach_sim、debug_attach_device、debug_breakpoint、debug_stack、debug_variables、debug_eval、debug_continue、debug_step、debug_detach |
ブレークポイントと変数インスペクションを含む完全なLLDB統合 |
| UIオートメーション | snapshot_ui、screenshot、tap、swipe、type_text |
自動操作とビジュアルキャプチャ |
| プロジェクトスキャフォールディング | create_project、add_file、add_package |
プロジェクトの作成と依存関係の追加 |
日常の開発で最も重要なツール:
-
build_sim— 何百回も呼び出すことになるツールです。ファイル、行番号、重要度ごとに分類されたエラーを含むJSONを返します。エージェントがエラーを読み取り、該当ファイルに移動し、何も触れなくても修正してくれます。 -
test_sim— テストメソッド単位の結果を返します。単に「テストが失敗しました」ではなく、どのテストがなぜ失敗したかをエージェントが正確に把握できます。 -
list_sims+boot_sim—xcrun simctlのフラグを暗記する必要のないシミュレータ管理です。エージェントが利用可能なランタイムを検出し、適切なデバイスを選択します。 -
discover_projs+list_schemes— プロジェクトのイントロスペクションです。エージェントがスキーム名やワークスペース構造を推測する必要がなくなります。 -
debug_attach_sim+debug_stack+debug_variables— リモートLLDBデバッグです。デバッガを開くことなく、エージェントがブレークポイントを設定し、変数を検査し、コードをステップ実行できます。
Apple Xcode MCP:Xcodeとの橋渡しとなる20ツール
AppleのMCPサーバーはXcode 26.3にxcrun mcpbridgeとして同梱されています。XPC(Appleのプロセス間通信フレームワーク)を通じて実行中のXcodeプロセスと通信し、CLIツールではアクセスできない内部状態を公開します。
インストール:
# Standard installation (global)
claude mcp add --transport stdio xcode \
-s user -- xcrun mcpbridge
# For Codex CLI
codex mcp add xcode -- xcrun mcpbridge
Xcode 26.3以上と実行中のXcodeプロセスが必要です。 Xcodeが開いていない場合、このサーバーを経由するすべてのMCP呼び出しが失敗またはハングします。XcodeBuildMCPにはこの制限はありません。
ツール一覧(5カテゴリにわたる20ツール):
| カテゴリ | ツール | 機能 |
|---|---|---|
| ファイル操作 | XcodeRead、XcodeWrite、XcodeUpdate、XcodeGlob、XcodeGrep |
Xcodeプロジェクトコンテキスト内でのファイル読み書き |
| ビルドとテスト | BuildProject、GetBuildLog、RunAllTests、RunSomeTests |
Xcodeの内部ビルドシステムによるビルドとテスト |
| 診断 | XcodeListNavigatorIssues、XcodeRefreshCodeIssuesInFile |
リアルタイムのコード診断(ビルドエラーだけではない) |
| コードとドキュメント | ExecuteSnippet、DocumentationSearch |
Swift REPLの実行とAppleドキュメント検索 |
| プレビュー | RenderPreview |
ヘッドレスSwiftUIプレビューレンダリング |
Apple MCP固有のツール(XcodeBuildMCPでは利用不可):
-
DocumentationSearch— WWDCセッションを含むAppleの開発者ドキュメントを検索します。Apple APIに関する質問では、Web検索よりも高速かつ信頼性が高いです。「HKQuantityType(.dietaryWater)は有効か?」と尋ねれば、ソースから決定的な回答が得られます。 -
ExecuteSnippet— プロジェクトのコンテキスト内でSwift REPLを実行します。フルビルドなしに、APIの動作確認、型変換のテスト、式の検証をエージェントが行えます。 -
RenderPreview— SwiftUIプレビューをヘッドレスでレンダリングします。ビューがエラーなくレンダリングされるかを確認できますが、視覚的な正確性の評価はできません(レンダリング結果はデータとして返され、視覚的に検査されるわけではありません)。 -
XcodeListNavigatorIssues— ビルドエラーだけでなく、Xcodeのアナライザからリアルタイムの診断情報を返します。未使用変数、潜在的なリテインサイクル、非推奨の警告など、ビルドシステムでは表面化しない問題を検出します。
両サーバーが必要な理由
ビルドとテストでは重複していますが、根本的に異なります:
┌─────────────────────────────────────────────────────────────────┐
│ MCP TOOL COVERAGE │
├─────────────────────────────────────────────────────────────────┤
│ │
│ XcodeBuildMCP (59 tools) Apple Xcode MCP (20 tools) │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ Standalone │ │ Requires Xcode │ │
│ │ (no Xcode process) │ │ (XPC bridge) │ │
│ │ │ │ │ │
│ │ ✓ Simulators │ BOTH │ ✓ Documentation │ │
│ │ ✓ Real devices │ ┌─────┐ │ ✓ Swift REPL │ │
│ │ ✓ LLDB debugging │ │Build│ │ ✓ SwiftUI previews │ │
│ │ ✓ UI automation │ │Test │ │ ✓ Live diagnostics │ │
│ │ ✓ Project scaffold │ └─────┘ │ ✓ Analyzer issues │ │
│ │ ✓ Screenshot │ │ │ │
│ └─────────────────────┘ └─────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
XcodeBuildMCPの用途: ビルド・テスト・デバッグのサイクルです。Xcodeを開かずに動作し、システムメモリの消費が少なく、より豊富なシミュレータおよびデバイス管理機能を提供します。これがメインのビルドツールとなります。
Apple Xcode MCPの用途: ドキュメント検索、Swift REPLによる検証、SwiftUIプレビューレンダリング、リアルタイム診断です。これらの機能が必要なセッションではXcodeを開いたままにしておきましょう。
実際の使い方: 筆者はMCP呼び出しの約90%にXcodeBuildMCPを使用し、ドキュメントとREPL検証にApple Xcode MCPを使用しています。ビルドとテストではエージェントがXcodeBuildMCPをデフォルトで使用します。Xcodeプロセスのオーバーヘッドがなく高速で、XPC依存がないため信頼性も高いからです。
検証
両サーバーのインストール後、接続を確認します:
# List all configured MCP servers
claude mcp list
# Expected output includes:
# XcodeBuildMCP: npx -y xcodebuildmcp@latest mcp - Connected
# xcode: xcrun mcpbridge - Connected
サーバーが「Disconnected」と表示される場合や表示されない場合:
- XcodeBuildMCPが接続できない場合: Node.jsがインストールされているか確認してください(
node --version)。npxコマンドにはNode.js 18以上が必要です。 - Apple Xcode MCPが接続できない場合: Xcode 26.3以上がインストールされており、ターミナルで
xcrun mcpbridgeコマンドが動作することを確認してください。ライセンス契約に同意するため、少なくとも一度はXcodeを開く必要があります。 - 両方とも表示されない場合: Claude Codeを再起動してください(新しいターミナルで
claudeを実行)。セッション中に登録されたMCPサーバーは、再起動するまで表示されないことがあります。
エージェントにMCPを使わせるための設定
MCPサーバーのインストールは必要条件ですが、それだけでは不十分です。明示的なガイダンスがなければ、エージェントはBash経由でxcodebuildを直接実行したり(非構造化出力でコンテキストトークンの無駄遣い)、Appleドキュメントの検索にWeb検索を使用したり(低速で信頼性が低い)する可能性があります。
CLAUDE.mdまたはエージェント定義に以下を追加してください:
## ビルドとテスト — 常にMCPを使用する
すべてのビルド操作には、シェルコマンドではなくMCPツールを優先してください:
- **ビルド**: `build_sim` / `build_device`(Bash経由の`xcodebuild`は使用しない)
- **テスト**: `test_sim` / `test_device`(Bash経由の`xcodebuild test`は使用しない)
- **シミュレーター**: `list_sims`、`boot_sim`、`open_sim`(Bash経由の`xcrun simctl`は使用しない)
- **デバッグ**: `debug_attach_sim`、`debug_stack`、`debug_variables`
- **Appleドキュメント**: `DocumentationSearch`(Apple APIの検索にWebSearchは使用しない)
- **Swift検証**: `ExecuteSnippet`(Bash経由の`swift`は使用しない)
- **プレビュー**: `RenderPreview`でヘッドレスSwiftUI検証
MCPは構造化されたJSONを返します。Bashは非構造化テキストを返します。
構造化データにより、トークン消費が抑えられ、エラー診断の精度も向上します。
このガイダンスにより、エージェントはまずMCPツールを選択するようになります。これがないと、エージェントがBash経由で長いxcodebuildコマンドを組み立て、出力の解析に数千のコンテキストトークンを消費し、実際のエラーを誤認識することがあります。
iOS プロジェクトにおけるCLAUDE.mdのパターン
CLAUDE.mdは、エージェント支援開発においてプロジェクト内で最も重要なファイルです。エージェントにとってのオンボーディングドキュメントであり、アーキテクチャドキュメントを読んだ新入社員と、推測で動く新入社員の違いを生み出します。
私が管理するすべてのiOSプロジェクトにはCLAUDE.mdがあります。以下は、8つのアプリすべてから得た効果的なパターンです。
必須セクション
すべてのiOS向けCLAUDE.mdには、以下の6つのセクションが必要です。それ以外はオプションです。
1. プロジェクトの基本情報
# Return - Zen Focus Timer
**Bundle ID:** `com.941apps.Return`
**Target:** iOS 26+ / macOS Tahoe / watchOS 26+ / tvOS 26+
**Architecture:** SwiftUI with @Observable pattern, companion Watch and TV apps
**Swift version:** 6.2
**Minimum deployment:** iOS 26.0
これが重要な理由:エージェントはコードを書く前にデプロイメントターゲットを把握する必要があります。iOS 17をターゲットとするエージェントはNavigationViewと@ObservedObjectを使用しますが、iOS 26をターゲットとするエージェントはNavigationStackと@Observableを使用します。バンドルIDはエンタイトルメントとHealthKit設定に関わります。Swiftバージョンによって、並行処理モデル(async/await vs. コンプリーションハンドラー、厳密な並行処理 vs. 緩い並行処理)が決まります。
2. 目的を注記したファイル構成
## File Structure
Return/ ├── ReturnApp.swift # App entry, dark mode enforcement ├── ContentView.swift # Main timer view with theme backgrounds ├── TimerManager.swift # Timer state, logic, and repeat handling ├── AudioManager.swift # Sound playback with AVAudioPlayer ├── Settings.swift # Centralized settings with validation ├── SettingsSheet.swift # Settings UI ├── HealthKitManager.swift # Mindful session logging + cross-device sync ├── LiveActivityManager.swift # Lock Screen/Dynamic Island ├── Theme.swift # Theme definitions ├── ThemeManager.swift # Theme state management ├── VideoBackgroundView.swift # AVPlayer video backgrounds ├── GlassTextShape.swift # Core Text glyph paths for glass effect ├── GlassTimerText.swift # Timer text with glass material └── Constants.swift # App constants
各ファイル名の後のインラインコメントは飾りではありません。これこそが最も効果の高いドキュメントです。新機能をどのファイルに追加すべきかをエージェントが判断する際、これらの注記があれば、すべてのファイルを読み込む代わりに、最初の試行で正しいファイルにたどり着けます。
アンチパターン: 注記なしのファイル一覧。TimerManager.swiftだけでは、状態管理なのかUIなのか、それとも両方なのかがエージェントには分かりません。TimerManager.swift # Timer state, logic, and repeat handlingと書けば、そこに何が属し、何が属さないかが明確になります。
3. ビルドとテストのコマンド
## Build & Test
Build for iOS simulator:
```bash
xcodebuild -scheme Return -destination 'platform=iOS Simulator,name=iPhone 16 Pro' build
Run tests:
xcodebuild -scheme Return -destination 'platform=iOS Simulator,name=iPhone 16 Pro' test
Run tvOS tests:
xcodebuild -scheme ReturnTV -destination 'platform=tvOS Simulator,name=Apple TV' test
Prefer MCP tools (build_sim, test_sim) over these raw commands.
MCP returns structured JSON with categorized errors.
エージェントがMCPを優先すべきとはいえ、生のコマンドも記載しておきましょう。フォールバック用のドキュメントとして機能し、スキーム名やデスティネーションが明示されます。
#### 4. 主要パターンとルール
```markdown
## Key Patterns
### Observable Architecture
- ALL view models use `@Observable` (NEVER `ObservableObject`)
- ALL navigation uses `NavigationStack` (NEVER `NavigationView`)
- State management via `@Observable` classes with `@MainActor` isolation
### Settings Pattern
- Centralized `Settings.shared` singleton
- All settings bounded to valid ranges with validation
- Sound names validated against whitelist
- Thread-safe access via @MainActor
### Audio System
- `AVAudioPlayer` with `.playback` category (plays in silent mode)
- Silent audio loop for background execution
- Bell playback with completion callbacks and token-based staleness
これらのパターンを明記することで、エージェントが不整合を持ち込むのを防げます。パターンの明文化がなければ、エージェントがあるファイルではObservableObjectを使い、別のファイルでは@Observableを使ったり、既存のSettings.sharedシングルトンを使わずに新しい設定の仕組みを作ったりすることがあります。
5. エージェントが絶対にやってはいけないこと
## Rules
- **NEVER modify .pbxproj files** — create Swift files, then I will add them to Xcode manually
- **NEVER modify .xcodeproj/ contents directly**
- **NEVER add new package dependencies** without asking first
- **NEVER change the deployment target**
- **NEVER modify entitlements files** unless explicitly asked
- **NEVER use NavigationView** — always NavigationStack
- **NEVER use ObservableObject** — always @Observable
- **NEVER use @StateObject** — always @State with @Observable
明示的な禁止事項は、暗黙の期待よりも効果的です。エージェントは否定的な制約を肯定的な提案より確実に守ります。禁止事項は二値的(やる/やらない)であるのに対し、提案はヒューリスティック的(こちらを優先する/場合によってはそちらを使う)だからです。
6. フレームワーク固有のコンテキスト
このセクションはアプリによって異なります。設定が自明でないフレームワークを使用している場合に記載してください:
HealthKitアプリの場合:
## HealthKit Configuration
- Entitlement: `com.apple.developer.healthkit`
- Info.plist keys:
- `NSHealthShareUsageDescription`: "Return reads your mindful minutes..."
- `NSHealthUpdateUsageDescription`: "Return logs meditation sessions..."
- Category types: `HKCategoryType(.mindfulSession)`
- Authorization checked on every write (user can revoke at any time)
- HealthKit is unavailable on tvOS — guard with `#if canImport(HealthKit)`
SwiftDataアプリの場合:
## SwiftData Models
### Model Relationships
- `GroceryList` has many `GroceryItem` (cascade delete)
- `GroceryItem` belongs to one `GroceryList`
- `GroceryItem` has optional `Category`
### Model Container Setup
- Configured in App struct with `modelContainer(for:)`
- Schema versioning: currently V2
- Migration plan: `GroceryMigrationPlan` handles V1 → V2
### Queries
- `@Query(sort: \GroceryItem.name)` for sorted fetches
- `@Query(filter: #Predicate { !$0.isCompleted })` for active items
- Always use `@Query` in views, `modelContext.fetch()` in managers
SpriteKitアプリの場合:
## SpriteKit Scene Hierarchy
GameScene (SKScene) ├── backgroundLayer (SKNode, zPosition: -100) │ └── StarfieldNode (custom, parallax scrolling) ├── gameLayer (SKNode, zPosition: 0) │ ├── playerShip (PlayerNode, zPosition: 10) │ ├── enemyContainer (SKNode, zPosition: 5) │ └── bulletPool (SKNode, zPosition: 8) ├── effectsLayer (SKNode, zPosition: 50) │ └── ParticleManager (manages explosion/trail emitters) └── hudLayer (SKNode, zPosition: 100) ├── scoreLabel (SKLabelNode) └── healthBar (HealthBarNode)
- Physics categories defined in `PhysicsCategory.swift` as bitmasks
- Contact detection via `didBegin(_ contact:)` on GameScene
- Bullet pooling: pre-allocate 50, recycle via `removeFromParent()` + re-add
Metalアプリの場合:
## Metal Pipeline
- Render pipeline: `MetalView` → `Renderer` → `ShaderLibrary`
- Compute pipeline: `AudioAnalyzer` → compute shader → texture output
- Shared uniforms struct: `Uniforms` in `ShaderTypes.h` (bridged to Swift)
- Frame timing: `CADisplayLink` drives render loop
- Buffer triple-buffering: 3 in-flight frames with semaphore
### Shader Files
- `Shaders.metal` — Main render shaders (vertex + fragment)
- `Compute.metal` — Audio analysis compute kernel
- `PostProcess.metal` — Bloom and color grading
### DO NOT modify Metal shaders without testing on device.
Simulator Metal is not representative of device GPU behavior.
実際のCLAUDE.md:Banana List(SwiftUI + SwiftData + iCloud + MCPサーバー)
以下は、6つのセクションすべてがどのように連携するかを示す注釈付きの実例です。これは、iCloud同期とカスタムMCPサーバーを備えた53ファイルの買い物リストアプリ、Banana Listで使用しているCLAUDE.mdのパターンです。MCPサーバーにより、アプリのデータがClaude Desktopに公開されます:
# Banana List - Grocery List App
**Bundle ID:** `com.941apps.BananaList`
**Target:** iOS 26+
**Architecture:** SwiftUI + SwiftData + iCloud Drive sync
**Swift version:** 6.2
**Minimum deployment:** iOS 26.0
## Core Features
- Grocery lists with items, categories, and quantities
- iCloud Drive sync via SwiftData CloudKit integration
- Custom MCP server exposing list data to Claude Desktop
- Liquid Glass design system
- Haptic feedback on interactions
- Share sheets for list sharing
## ファイル構成
BananaList/ ├── BananaListApp.swift # App entry, model container setup ├── Models/ │ ├── GroceryList.swift # @Model: list with name, items, color │ ├── GroceryItem.swift # @Model: item with name, quantity, category, isCompleted │ ├── Category.swift # @Model: user-defined categories │ └── SampleData.swift # Preview and test data ├── Views/ │ ├── ListsView.swift # Main list of grocery lists │ ├── ListDetailView.swift # Items within a list │ ├── ItemRow.swift # Single item row with swipe actions │ ├── AddItemSheet.swift # New item form │ ├── CategoryPicker.swift # Category selection with create-new │ └── SettingsView.swift # App settings ├── Managers/ │ ├── CloudSyncManager.swift # iCloud Drive sync status and conflict resolution │ └── HapticManager.swift # UIImpactFeedbackGenerator wrapper ├── MCP/ │ ├── MCPServer.swift # MCP server for Claude Desktop integration │ ├── ListTools.swift # MCP tools: list CRUD operations │ └── ItemTools.swift # MCP tools: item CRUD operations └── Extensions/ ├── Color+Extensions.swift # Custom color definitions └── View+Extensions.swift # Reusable view modifiers
## SwiftData モデル
### リレーションシップ
- `GroceryList` は複数の `GroceryItem` を持つ(カスケード削除)
- `GroceryItem` は1つの `GroceryList` に属する(必須)
- `GroceryItem` はオプショナルの `Category` を持つ
- `Category` は複数の `GroceryItem` を持つ(削除時にnullify)
### コンテナのセットアップ
```swift
@main
struct BananaListApp: App {
var body: some Scene {
WindowGroup {
ListsView()
}
.modelContainer(for: [GroceryList.self, GroceryItem.self, Category.self])
}
}
クエリパターン
- リスト一覧:
@Query(sort: \GroceryList.name) var lists: [GroceryList] - 未完了アイテム:
@Query(filter: #Predicate { !$0.isCompleted }) - カテゴリ別: フェッチ後にメモリ内でフィルタリング(SwiftData のPredicate制限のため)
ビルドとテスト
xcodebuild -scheme BananaList -destination 'platform=iOS Simulator,name=iPhone 16 Pro' build
xcodebuild -scheme BananaList -destination 'platform=iOS Simulator,name=iPhone 16 Pro' test
生のコマンドよりも MCP ツール(build_sim、test_sim)の使用を推奨します。
主要パターン
Observable + SwiftData
- SwiftData の
@Modelクラスは自動的にObservableとなります @Modelクラスに@Observableを追加しないでください(冗長であり、警告の原因になります)- フォームでモデルプロパティへの双方向バインディングには
@Bindableを使用してください - ビュー内では
@Queryを、ビュー以外のコードではmodelContext.fetch()を使用します
iCloud同期
- SwiftData CloudKit統合による自動同期
- 競合解決: last-write-wins(CloudKitのデフォルト)
- 同期ステータスは
CloudSyncManager.shared.syncStateで確認できます - 同じiCloudアカウントで2台のシミュレータを実行して同期をテストしてください
MCP サーバーアーキテクチャ
- ポート8765でローカル WebSocket サーバーとして動作します
- 6つのツールを公開: listAll、getList、createList、addItem、completeItem、deleteItem
- Claude Desktopは
~/.config/claude-desktop/config.jsonの MCP 設定を通じて接続します
ルール
- .pbxproj や .xcodeproj の内容を絶対に変更しないこと
- SampleData.swift を更新せずにモデルスキーマを変更しないこと
ObservableObjectは使用禁止 — SwiftData モデルはすでにObservableです@StateObjectは使用禁止 —@Observableクラスには@Stateを使用することNavigationViewは使用禁止 — 常にNavigationStackを使用すること@Modelクラスに@Observableマクロを追加しないこと- フォームでモデルプロパティにバインドする際は必ず
@Bindableを使用すること - iCloud同期の変更は必ず2台のシミュレータインスタンスでテストすること
### 実際のCLAUDE.md: Reps(最小限のSwiftDataアプリ — 14ファイル)
小規模プロジェクトでは、CLAUDE.mdを簡潔にまとめられます。ここでは14ファイルのワークアウトトラッカー「Reps」のパターンを紹介します。短いCLAUDE.mdでも6つの必須セクションすべてをカバーしている点に注目してください:
```markdown
# Reps - Workout Tracking
**Bundle ID:** `com.941apps.Reps`
**Target:** iOS 26+
**Architecture:** SwiftUI + SwiftData
**Swift version:** 6.2
## File Structure
Reps/ ├── RepsApp.swift # App entry, model container ├── Models/ │ ├── Workout.swift # @Model: workout with exercises, date, duration │ ├── Exercise.swift # @Model: exercise with sets, reps, weight │ └── ExerciseTemplate.swift # @Model: saved exercise definitions ├── Views/ │ ├── WorkoutListView.swift # Main list of workouts │ ├── WorkoutDetailView.swift # Exercises within a workout │ ├── ExerciseRow.swift # Single exercise with inline editing │ ├── AddExerciseSheet.swift # Exercise selection from templates │ ├── NewWorkoutView.swift # Start new workout flow │ └── StatsView.swift # Progress charts and summaries ├── Managers/ │ └── WorkoutTimer.swift # Active workout timer └── Extensions/ └── Date+Extensions.swift # Formatting helpers
## Build & Test
```bash
xcodebuild -scheme Reps -destination 'platform=iOS Simulator,name=iPhone 16 Pro' build
xcodebuild -scheme Reps -destination 'platform=iOS Simulator,name=iPhone 16 Pro' test
SwiftData Relationships
Workouthas manyExercise(cascade delete)Exercisehas optionalExerciseTemplateExerciseTemplatestandalone (nullify on exercise delete)
Rules
- NEVER modify .pbxproj
- NEVER use ObservableObject — use @Observable
- NEVER use NavigationView — use NavigationStack
- @Model classes are already Observable — do not add @Observable macro
- Use @Bindable for form bindings to model properties
40行のCLAUDE.mdで14ファイルのプロジェクトをカバーできます。作成に10分かかりますが、エージェントの混乱を何時間も節約できるでしょう。
### 実際のCLAUDE.md: Starfield Destroyer(SpriteKit + Metal — 32ファイル)
ゲームプロジェクトでは、フレームワーク固有のコンテキストがより多く必要です。エージェントはシーングラフ、物理カテゴリ、ゲームステートマシンを理解する必要があります:
```markdown
# Starfield Destroyer - Space Shooter
**Bundle ID:** `com.941apps.StarfieldDestroyer`
**Target:** iOS 26+
**Architecture:** SpriteKit + Metal post-processing + Game Center
**Swift version:** 6.2
## Game Overview
99 levels across 3 galaxies. 8 unlockable ships with different stats.
Game Center leaderboards and achievements. Metal shader post-processing
for bloom and screen effects.
## File Structure
StarfieldDestroyer/ ├── StarfieldDestroyerApp.swift # App entry, Game Center auth ├── GameScene.swift # Main game scene, update loop ├── MenuScene.swift # Title screen, ship selection ├── Entities/ │ ├── PlayerShip.swift # Player node with physics, weapons, shields │ ├── EnemyShip.swift # Enemy base class with AI behaviors │ ├── Bullet.swift # Bullet pool node │ ├── PowerUp.swift # Collectible power-ups │ └── Boss.swift # Boss enemies (levels 33, 66, 99) ├── Systems/ │ ├── LevelManager.swift # Level progression, wave spawning │ ├── PhysicsCategory.swift # UInt32 bitmask categories │ ├── CollisionHandler.swift # Contact delegate methods │ ├── ScoreManager.swift # Score tracking, multipliers │ ├── ParticleManager.swift # Explosion, trail, shield emitters │ └── AudioManager.swift # Sound effects, background music ├── UI/ │ ├── HUDNode.swift # Score, health, level display │ ├── ShipSelectView.swift # SwiftUI ship selection (UIHostingController) │ ├── GameOverView.swift # Game over screen with score submission │ └── PauseMenu.swift # Pause overlay ├── Metal/ │ ├── MetalRenderer.swift # Post-processing render pipeline │ ├── BloomShader.metal # Bloom post-process effect │ └── ShaderTypes.h # Shared uniforms (bridging header) ├── Data/ │ ├── ShipData.swift # 8 ship definitions (speed, damage, shields) │ ├── LevelData.swift # 99 level configurations │ └── AchievementData.swift # Game Center achievement definitions └── GameCenterManager.swift # Leaderboard/achievement submission
## SpriteKit Scene Hierarchy
GameScene (SKScene) ├── backgroundLayer (zPosition: -100) │ └── StarfieldNode (parallax scrolling, 3 layers) ├── gameLayer (zPosition: 0) │ ├── playerShip (zPosition: 10) │ ├── enemyContainer (zPosition: 5) │ ├── bulletPool (zPosition: 8) — pre-allocated 50 bullets │ └── powerUpContainer (zPosition: 3) ├── effectsLayer (zPosition: 50) │ └── ParticleManager (explosion + trail emitters) └── hudLayer (zPosition: 100) ├── scoreLabel (SKLabelNode) ├── healthBar (custom SKShapeNode) └── levelLabel (SKLabelNode)
## Physics Categories
```swift
struct PhysicsCategory {
static let none: UInt32 = 0
static let player: UInt32 = 0b1 // 1
static let enemy: UInt32 = 0b10 // 2
static let bullet: UInt32 = 0b100 // 4
static let powerUp: UInt32 = 0b1000 // 8
static let shield: UInt32 = 0b10000 // 16
static let bossBullet:UInt32 = 0b100000 // 32
}
// Contact pairs:
// player + enemy → damage
// player + powerUp → collect
// bullet + enemy → destroy
// player + bossBullet → damage
ゲームステートマシン
.menu → .playing → .paused → .playing
→ .gameOver → .menu
→ .bossIntro → .playing
→ .levelComplete → .playing (next level)
Metal ポストプロセッシング
- ブルームシェーダー:
BloomShader.metal— マルチパスガウシアンブラー + 加算ブレンド - ユニフォーム:
PostProcessUniforms { float intensity; float threshold; float2 resolution; } - SpriteKitが各フレームをレンダリングした後、
SKView.presentScene(:transition:)を通じて適用されます - 実機でのテストなしにMetalシェーダーを変更しないでください
ビルドとテスト
xcodebuild -scheme StarfieldDestroyer -destination 'platform=iOS Simulator,name=iPhone 16 Pro' build
xcodebuild -scheme StarfieldDestroyer -destination 'platform=iOS Simulator,name=iPhone 16 Pro' test
## ルール
- .pbxproj を絶対に変更しないでください
- PhysicsCategory のビットマスクを絶対に変更しないでください(すべての衝突検出が壊れます)
- レンダリング順序を理解せずにシーン階層のZオーダーを変更しないでください
- SwiftとMetalの両方の参照を更新せずに ShaderTypes.h を変更しないでください
- 新しい敵を追加する場合は、EnemyShip を変更するのではなく、サブクラス化してください
- 弾丸プーリング:removeFromParent() + 再追加でリサイクルし、新規割り当ては行わないでください
- Game Center:スコア送信前に必ず isAuthenticated を確認してください
実際の CLAUDE.md:amp97(Metal + オーディオビジュアライゼーション — 41ファイル)
Metalプロジェクトでは、エージェントが視覚的な出力を検証できないため、最もフレームワーク固有のコンテキストが必要となります。
# amp97 - Audio Visualizer
**Bundle ID:** `com.941apps.amp97`
**Target:** iOS 26+
**Architecture:** Metal render pipeline + AVAudioEngine analysis
**Swift version:** 6.2
## Architecture
Audio Input (microphone/file) → AVAudioEngine tap → FFT (vDSP) → Frequency/amplitude buffers → Metal compute shader (analysis) → Metal render pipeline (visualization) → CADisplayLink (60fps) → MTKView
## File Structure
amp97/ ├── amp97App.swift # App entry ├── Audio/ │ ├── AudioEngine.swift # AVAudioEngine setup, tap installation │ ├── FFTProcessor.swift # vDSP FFT, frequency bin extraction │ ├── AudioBuffer.swift # Ring buffer for audio data │ └── MicrophoneManager.swift # Microphone permission, session config ├── Rendering/ │ ├── MetalView.swift # MTKView wrapper for SwiftUI │ ├── Renderer.swift # Main render loop, pipeline state │ ├── ShaderLibrary.swift # Compiled shader management │ ├── BufferManager.swift # Triple-buffered uniform updates │ └── TextureManager.swift # Offscreen render targets ├── Shaders/ │ ├── Shaders.metal # Vertex + fragment shaders │ ├── AudioCompute.metal # Audio analysis compute kernel │ ├── PostProcess.metal # Bloom, color grading │ └── ShaderTypes.h # Shared uniforms (bridging header) ├── Visualizations/ │ ├── WaveformViz.swift # Oscilloscope-style waveform │ ├── SpectrumViz.swift # Frequency spectrum bars │ ├── CircularViz.swift # Radial visualization │ └── VizSelector.swift # Visualization switching ├── Views/ │ ├── MainView.swift # Full-screen viz with overlays │ ├── ControlsOverlay.swift # Play/pause, viz selection, gain │ └── SettingsView.swift # Audio source, sensitivity └── Extensions/ ├── SIMD+Extensions.swift # Vector math helpers └── Color+Metal.swift # UIColor → float4 conversion
## Metal Pipeline
### Uniforms (ShaderTypes.h)
```c
typedef struct {
float time;
float2 resolution;
float audioLevel; // 0.0-1.0 RMS amplitude
float frequencyBins[64]; // FFT output, normalized
float4x4 transform;
} Uniforms;
Render Pipeline
- Compute pass: AudioCompute.metal processes FFT data → texture
- Render pass: Shaders.metal reads texture + uniforms → visualization
- Post-process pass: PostProcess.metal applies bloom → final output
Buffer Management
- Triple buffering with DispatchSemaphore(value: 3)
- Uniforms updated per-frame on CPU, consumed by GPU 1-2 frames later
- Audio data ring buffer: 4096 samples, lock-free single producer/consumer
Rules
- NEVER modify ShaderTypes.h without updating BOTH Swift and Metal sides
- NEVER exceed 64 frequency bins (fixed buffer size in shader)
- NEVER test Metal visual output in simulator — device only
- NEVER modify the audio engine tap format (48kHz, mono, float32)
- Triple buffer discipline: always signal semaphore in completion handler
- Audio session: .playAndRecord category with .defaultToSpeaker option
### プロジェクト規模に応じた CLAUDE.md のスケーリング
適切な詳細レベルは、ファイル数とフレームワークの複雑さによって異なります。
| プロジェクト規模 | CLAUDE.md の深さ | 例 |
|-------------|----------------|---------|
| **小規模(20ファイル未満)** | アイデンティティ + ファイルリスト + ルール | Reps(14ファイル):基本的な SwiftData パターン、ビルドコマンド、禁止事項 |
| **中規模(20〜40ファイル)** | + フレームワークコンテキスト + 主要パターン | TappyColor(30ファイル):SpriteKit シーン階層、物理カテゴリ、ゲームループ |
| **大規模(40ファイル以上)** | + アーキテクチャ図 + 関係マップ + マルチターゲット情報 | Return(63ファイル):クロスプラットフォームアーキテクチャ、セッション同期図、プラットフォーム間の差異 |
| **特殊(Metal/GPU)** | + パイプライン図 + 共有型定義 + バッファレイアウト | amp97(41ファイル):レンダーパイプラインステージ、Uniform構造体、バッファ管理 |
ドキュメントを書きすぎるコストはほぼゼロです(エージェントは不要な部分をスキップします)。一方、ドキュメントが不足するコストは高くつきます(エージェントがコードベースと矛盾するパターンを勝手に作り出してしまいます)。
### CLAUDE.md チェックリスト
iOSプロジェクトの CLAUDE.md を作成または監査する際に、このチェックリストを活用してください。
- [ ] Bundle IDとデプロイメントターゲットが明記されている
- [ ] Swiftバージョンとアーキテクチャパターンが記載されている
- [ ] 各ファイルの目的を示すインライン注釈付きのファイル構造がある
- [ ] 正しいスキームとデスティネーションを指定したビルドコマンドがある
- [ ] 正しいスキームとデスティネーションを指定したテストコマンドがある
- [ ] MCP の優先設定が記載されている(「xcodebuild より build_sim を優先」など)
- [ ] @Observable ルール(ObservableObject は使用禁止)
- [ ] NavigationStack ルール(NavigationView は使用禁止)
- [ ] .pbxproj の変更禁止
- [ ] フレームワーク固有のコンテキスト(HealthKit パーミッション、SwiftData リレーションシップ、SpriteKit 階層、Metal パイプライン)
- [ ] プラットフォーム可用性ガードのドキュメント(#if canImport、#if os)
- [ ] 主要なシングルトンと共有パターンのドキュメント
- [ ] 既知の制限事項や注意点の記載
---
## 最初のエージェントセッション
MCP を設定し、プロジェクトに CLAUDE.md を配置したら、効果的な最初のセッションのウォークスルーを見てみましょう。ここでは Claude Code CLI を使用していますが、ワークフロー自体はどのランタイムにも適用できます。
### ステップ1:エージェントがプロジェクトを認識できるか確認する
You: Summarize this project. What scheme should I build with?
Claude: [reads CLAUDE.md] [calls discover_projs via MCP] [calls list_schemes via MCP]
This is Return, a zen meditation timer for iOS 26+, watchOS, and tvOS.
Available schemes: Return, ReturnWatch, ReturnTV, ReturnTests.
Primary scheme for iOS: Return.
エージェントが CLAUDE.md の内容を参照しない場合は、ファイルがプロジェクトルート(`.xcodeproj` または `Package.swift` と同じディレクトリ)に配置されているか確認してください。
### ステップ2:ヘルスチェックビルドを実行する
You: Build the Return scheme for iOS simulator and report results.
Claude: [calls list_sims — finds iPhone 16 Pro on iOS 26.2] [calls boot_sim — boots the simulator] [calls build_sim — builds the project]
Build succeeded. 63 source files compiled.
2 warnings:
- HomeView.swift:132 — UIScreen.main deprecated in iOS 26.0
- LogWaterIntent.swift:61 — Result of try? is unused
0 errors.
### ステップ3:テストを実行する
You: Run all tests and report pass/fail.
Claude: [calls test_sim]
12 tests executed:
✓ TimerManagerTests (5/5 passed)
✓ SettingsTests (4/4 passed)
✓ ConstantsTests (3/3 passed)
All tests passed.
### ステップ4:機能を実装する
You: Add a new “Rain” theme to the theme system. Follow the existing pattern in Theme.swift and ThemeManager.swift. Include a rain ambient sound. Build and test after implementation.
Claude: [reads Theme.swift — understands Theme struct pattern] [reads ThemeManager.swift — understands theme registration] [reads existing theme definitions] [adds Rain theme to Theme.swift following existing pattern] [registers Rain theme in ThemeManager.swift] [calls build_sim — build succeeds] [calls test_sim — all tests pass]
Added Rain theme:
- Theme.swift: new `static let rain` with video, cover, ambient
- ThemeManager.swift: added to `allThemes` array
- Build: success (0 errors, 2 existing warnings)
- Tests: 12/12 passed
Note: You'll need to add rain_ambient.wav to the project
and add the new Theme.swift changes to the Xcode target manually
(I did not modify .pbxproj).
エージェントが .pbxproj を変更しなかったという注記は、CLAUDE.md に記載されたルールの結果です。このルールがなければ、エージェントはプロジェクトファイルを変更しようとし、おそらく破損させてしまうでしょう。
---
## iOSでエージェントが得意とする領域
以下のタスクでは、エージェントが一貫して正確かつプロダクション品質のコードを生成し、人間によるレビューも最小限で済みます。
### SwiftUIのビューとモディファイア
エージェントはSwiftUIの宣言的構文に対して高いパターン認識能力を持っています。ビューの構成、モディファイアチェーン、状態バインディング、レイアウトなど、SwiftUIのAPIの表面積は十分にドキュメント化されており、パターンの一貫性が高いため、エージェントの学習データとよく適合します。
**エージェントが優れている点:**
- 説明からの新規ビュー構築(「X、Y、Zのトグル付き設定シートを作成して」)
- モディファイアチェーンの適用(`.glassEffect()`、`.sensoryFeedback()`、`.navigationTitle()`)
- レイアウトパターンの変換(VStackからLazyVGrid、ListからScrollViewへ)
- SwiftDataモデルへの`@Bindable`フォームバインディングの実装
- サンプルデータを含むプレビュープロバイダーの構築
**優れた結果を生むプロンプトの例:**
Create a SettingsView that matches the existing pattern in SettingsSheet.swift. Include toggles for: - Enable haptic feedback (Settings.shared.hapticsEnabled) - Enable HealthKit logging (Settings.shared.healthKitEnabled) - Show session history (navigation link to SessionHistoryView)
Use Liquid Glass styling with .glassEffect() on section backgrounds. Follow the @Observable pattern, not ObservableObject.
具体性が重要です。「設定ビューを作って」では汎用的な出力になりますが、「SettingsSheet.swiftの既存パターンに合わせたSettingsViewを作って」とすれば、コードベースと一貫性のある出力が得られます。
### SwiftDataのモデルとクエリ
エージェントはSwiftDataの`@Model`マクロ、リレーションシップ、`@Query`パターンを安定して処理できます。このフレームワークの宣言的な性質(Django ORMやSQLAlchemyに類似)は、エージェントが多くのコードベースで学習済みのパターンとよく合致します。
**エージェントが優れている点:**
- リレーションシップを含む`@Model`クラスの定義
- ソートディスクリプタと述語を用いた`@Query`の記述
- `modelContext`を使ったCRUD操作の実装
- スキーマバージョン間のマイグレーションプラン
- プレビューデータとテストフィクスチャ
**ガイダンスが必要な場面:**
- 複雑な`#Predicate`式(SwiftDataの述語DSLには制約があり、エージェントが把握していない場合があります。既知の制約はCLAUDE.mdに記載してください)
- CloudKit同期の設定(SwiftData経由で自動化されますが、エージェントが手動同期を実装しようとすることがあります)
### ユニットテスト
エージェントが書くユニットテストは、iOSプロジェクトにおいて一貫して高品質です。XCTestパターン、非同期テストメソッド、セットアップ/ティアダウンのライフサイクルを正しく理解しています。
Write unit tests for TimerManager covering: 1. Initial state is .stopped 2. start() transitions to .running 3. pause() transitions to .paused 4. reset() returns to .stopped with original duration 5. Timer counts down correctly (test with 3-second duration)
エージェントは`setUp()`と`tearDown()`を備えた構造化されたXCTestケースを生成し、適切なアサーションとタイマーベースのテスト向けの非同期処理も含まれます。
### リファクタリングとパターン適用
機械的なリファクタリングはエージェントの得意分野です。ビューのコンポーネント分割、`ObservableObject`から`@Observable`への変換、`NavigationView`から`NavigationStack`への移行、複数ファイルにわたる一貫したパターン適用などが該当します。
Refactor all views in the Views/ directory to use @Observable instead of ObservableObject. Update @StateObject to @State, @ObservedObject to direct property access, and @Published to plain properties.
エージェントは各ファイルを体系的に処理し、変換を正確に適用しながら既存の機能を維持します。手作業で1時間かかるリファクタリングが、ほぼ完璧な精度で数分のうちに完了する、非常にレバレッジの高い作業です。
### MCPによるビルドエラー診断
構造化されたMCP出力があれば、エージェントはほとんどの開発者より速くビルドエラーを診断できます。エラーJSONを読み取り、正確なファイルと行を特定し、エラーメッセージを理解して修正を適用します。多くの場合、1ターンで完了します。
**エージェントが自律的に修正できるエラー:**
- importの不足
- 型の不一致
- プロトコル準拠の欠落
- 非推奨のAPIの使用(代替APIへの置き換えを含む)
- 必須イニシャライザパラメータの欠落
- アクセス制御の違反
**エージェントに補助が必要なエラー:**
- 曖昧な型解決(複数モジュールが同じ型を定義している場合)
- 複雑なジェネリクス制約の失敗
- マクロ展開エラー(エージェントは展開後のマクロ出力を確認できません)
### シミュレータ管理
エージェントはMCPを通じたシミュレータのライフサイクル管理も得意としています:
Boot an iPhone 16 Pro simulator on iOS 26, install the app, and take a screenshot.
エージェントは`list_sims`で利用可能なランタイムを検索し、`boot_sim`でシミュレータを起動、`build_sim`でビルドとインストールを実行、`screenshot`でキャプチャを取得します。すべて構造化されたMCP呼び出しで処理されます。
---
## エージェントがiOS開発で苦手とすること
エージェントが失敗する領域を正直に整理しましょう。これらの限界を把握しておくことで、フラストレーションやトークンの無駄遣いを防げます。
### .pbxprojファイルの編集 — 絶対にやらないこと
これはiOSエージェント開発における最も重要なルールです。`.pbxproj`ファイルはXcodeのプロジェクト設定ファイルであり、UUID参照、ビルドフェーズのリスト、ターゲットメンバーシップを含む構造化テキストファイルです。名目上は人間が読める形式ですが、AIエージェントにとっては実質的に解析不可能です。
**エージェントが.pbxprojで失敗する理由:**
- 独自フォーマット(JSONでもYAMLでもXMLでもない)を使用しており、位置に意味があります
- すべてのエントリがUUIDで相互参照されており、ファイルを1つ追加するだけで3〜5箇所のセクションを整合性を保ちながら更新する必要があります
- 1文字でも間違えるとプロジェクトファイル全体が破損します
- Xcodeの.pbxprojマージコンフリクト解決はそもそも脆弱であり、エージェントの編集はそれをさらに悪化させます
**エージェントが.pbxprojを編集するとどうなるか:**
1. 編集は成功したように見えます(エージェントは「ファイルを更新しました」と報告します)
2. Xcodeがプロジェクトを開けなくなります(「プロジェクトファイルが破損しています」)
3. Gitの履歴から復旧するのに15〜60分を費やすことになります
4. PreToolUseフックを追加すべきだったと学びます([Hooks](#hooks-for-ios-development)を参照)
**正しいワークフロー:** エージェントがSwiftファイルを作成し、Xcodeプロジェクトへの追加は手動で行います(Xcodeにドラッグするか、File > Add Filesを使用)。1ファイルあたり5秒で済み、何時間もの復旧作業を防げます。
**Swift Package Managerプロジェクトの場合:** この制限はそれほど深刻ではありません。`Package.swift`は標準的なSwiftファイルであり、エージェントが確実に編集できます。プロジェクトがSPMのみ(.xcodeprojなし)を使用している場合、エージェントがプロジェクト構造全体を管理できます。
### 複雑なInterface Builder / Storyboardの編集
プロジェクトがInterface Builder(`.xib`ファイル)やStoryboard(`.storyboard`ファイル)を使用している場合、エージェントはこれらを意味のある形で編集できません。自動生成されるUUID、制約の参照、アウトレット接続を含むXMLファイルであり、テキスト編集ではなくビジュアル編集のために設計されています。
**対策:** 新しいビューにはSwiftUIのみを使用しましょう。プロジェクトにレガシーなInterface Builderファイルがある場合は、そのままにして新しいUIはSwiftUIで構築してください。
### パフォーマンス最適化
エージェントは正しいコードを書きますが、必ずしもパフォーマンスの良いコードとは限りません。アプリのプロファイリング、ボトルネックの特定、フレームレートの測定はできません。パフォーマンス最適化には以下が必要です:
1. Instrumentsによるプロファイリング(ビジュアルツールであり、エージェントからアクセスできません)
2. 特定デバイスのGPU/CPU特性の理解
3. 計測に基づく反復的な変更
**この問題が現れる場面:**
- Metalシェーダーの最適化(エージェントは有効なMetalコードを書きますが、GPUのフレームタイムを計測できません)
- SwiftUIビューボディの複雑さ(エージェントは深くネストされたビューを作成し、再描画のオーバーヘッドを引き起こします)
- Core Data / SwiftDataのフェッチ最適化(エージェントは正しいクエリを書きますが、大規模データセットでは遅くなる可能性があります)
**対策:** 実装にはエージェントを使い、Instrumentsで手動プロファイリングを行い、特定した最適化をエージェントに適用させましょう。
### コード署名とプロビジョニング
エージェントは、エラーメッセージを読む以上のコード署名問題のデバッグはできません。プロビジョニングプロファイルの管理、証明書の作成、エンタイトルメントの設定、App Storeへの提出は、Apple Developerポータル、キーチェーンアクセス、Xcodeの署名UIを必要とする、本質的に人間が操作するワークフローです。
**エージェントに見えるもの:** 「Signing for 'Return' requires a development team.」
**エージェントに見えないもの:** 証明書が期限切れかどうか、プロビジョニングプロファイルにデバイスが含まれているかどうか、バンドルIDがApp IDと一致しているかどうか、エンタイトルメントファイルが正しいかどうか。
**対策:** 署名関連はすべてXcodeのSigning & Capabilitiesタブで処理してください。署名の問題をエージェントにデバッグさせないようにしましょう。
### 複雑なMetalシェーダーのデバッグ
エージェントは構文的に正しいMetal Shading Language(MSL)を書けますが、視覚的な出力の検証やGPU側の問題のデバッグはできません。MetalシェーダーはGPU上で実行されるため、シェーダーが正しい視覚的結果を生成しているかどうかのフィードバックメカニズムがエージェントにはありません。
**エージェントがMetalでできること:**
- 記述からバーテックスシェーダーとフラグメントシェーダーを書く
- SwiftでMetalレンダーパイプラインをセットアップする
- データ並列処理のためのコンピュートシェーダーを作成する
- `.metal`ファイルのコンパイルエラーを修正する
**エージェントがMetalでできないこと:**
- シェーダー出力の視覚的な正確性を検証する
- GPUパフォーマンス(フレームタイム、占有率、メモリ帯域幅)をデバッグする
- 視覚的なアーティファクト(バンディング、精度の問題、不正なカラースペース)を診断する
- 異なるGPUアーキテクチャでのテスト(AシリーズとMシリーズの動作の違い)
**対策:** Metalシェーダーは実機でテストしてください。シミュレーターのMetal実装はデバイスのGPUの動作を正確に再現しません。視覚的なデバッグにはXcodeのGPU Frame Captureを使用してください。
### ビジュアルレイアウトの検証
エージェントはアプリのUIを見ることができません。SwiftUIのレイアウトコードを書いてコンパイルが通ることは確認できますが、表示結果が正しいかどうかは判断できません。10ピクセルずれた中央配置、間違ったフォントウェイト、重なり合った要素があっても、ビルドエラーは出ず、ロジックテストもすべてパスします。
**対策:** UIの変更はビジュアルで確認してください。レイアウトの検証にはXcodeのSwiftUI Previews(またはヘッドレスレンダリング用のApple MCPの`RenderPreview`)を使用しましょう。自動化されたビジュアルリグレッション検出には、swift-snapshot-testingなどのライブラリによるスナップショットテストの導入も検討してください。
---
## iOS開発向けフック
フックは、エージェントのワークフロー内の特定のタイミングで確定的に実行されるシェルコマンドです。これは強制メカニズムであり、「.pbxprojを編集しないでください」(エージェントが無視する可能性のある提案)と「.pbxprojを編集できません」(ハードブロック)の違いを生み出します。
フックシステムの背景については、[Claude Codeフックガイド](/blog/claude-code-hooks-tutorial)をご覧ください。このセクションでは、iOS固有のフックパターンを扱います。
### PreToolUse:.pbxproj書き込みのブロック
iOSプロジェクトにおいて最も重要なフックです。エージェントが`.pbxproj`ファイル、`.xcodeproj/`ディレクトリ、およびその他のXcode管理ファイルに書き込むことをブロックします:
```json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"command": "bash -c 'INPUT=$(cat); FP=$(echo \"$INPUT\" | jq -r \".tool_input.file_path // empty\"); if echo \"$FP\" | grep -qE \"\\.(pbxproj|xcworkspace|xib|storyboard)$|xcodeproj/|xcworkspace/\"; then echo \"BLOCKED: Do not modify Xcode project files. Create Swift files and add to Xcode manually.\" >&2; exit 2; fi'"
}
]
}
}
プロジェクトルートの.claude/settings.json、またはグローバル保護のために~/.claude/settings.jsonに配置してください。
動作の仕組み: エージェントがパターンに一致するファイルに対してEditまたはWriteツールを使用しようとすると、フックが実行され、ファイルパスを検出し、stderrに警告を出力して、終了コード2で終了します(これによりツールの使用がブロックされます)。エージェントはエラーメッセージを受け取り、アプローチを変更します。
検出対象:
- .pbxprojへの直接編集
- .xcodeproj/または.xcworkspace/ディレクトリ内のすべてのファイル
- Interface Builderファイル(.xib、.storyboard)
PostToolUse:SwiftFormatによる保存時フォーマット
エージェントがSwiftファイルを書き込みまたは編集するたびに、自動的にフォーマットを適用します:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"command": "bash -c 'INPUT=$(cat); FP=$(echo \"$INPUT\" | jq -r \".tool_input.file_path // empty\"); if echo \"$FP\" | grep -qE \"\\.swift$\"; then swiftformat \"$FP\" --quiet 2>/dev/null; fi'"
}
]
}
}
前提条件: SwiftFormatがインストールされている必要があります(brew install swiftformat)。
なぜ重要なのか: エージェントは構文的に正しいSwiftコードを生成しますが、フォーマット規約に一貫して従うとは限りません。SwiftFormatはインデント、ブレースの配置、importの順序を正規化します。保存時フォーマットフックにより、エージェントが触れたすべてのSwiftファイルは、確認する前に自動的にフォーマットされます。
オプション:プロジェクトルートに.swiftformat設定ファイルを追加して、フォーマットルールをカスタマイズできます:
# .swiftformat
--indent 4
--allman false
--stripunusedargs closure-only
--importgrouping testable-bottom
--header strip
PostToolUse:SwiftLintの自動実行
SwiftLintを使用している場合、Swiftファイルの編集後に毎回実行できます:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"command": "bash -c 'INPUT=$(cat); FP=$(echo \"$INPUT\" | jq -r \".tool_input.file_path // empty\"); if echo \"$FP\" | grep -qE \"\\.swift$\"; then swiftlint lint --path \"$FP\" --quiet 2>/dev/null || true; fi'"
}
]
}
}
|| trueにより、Lintの警告がエージェントをブロックするのを防いでいます。Lint違反でブロックしたい場合は、これを削除してください。
PostToolUse:変更後の自動ビルド
積極的なフィードバックループを実現するために、Swiftファイルの変更後にビルドをトリガーできます:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"command": "bash -c 'INPUT=$(cat); FP=$(echo \"$INPUT\" | jq -r \".tool_input.file_path // empty\"); if echo \"$FP\" | grep -qE \"\\.swift$\"; then xcodebuild -scheme Return -destination \"platform=iOS Simulator,name=iPhone 16 Pro\" build 2>&1 | tail -5; fi'"
}
]
}
}
注意: これはコストの高い操作です。ファイル編集のたびにビルドがトリガーされます。使用は控えめにしましょう。即時のビルドフィードバックが必要なデバッグセッション中に最も有効です。通常の開発では、エージェントの準備が整った時点でMCPを使って手動でビルドをトリガーさせるのが適切です。
PreToolUse:エンタイトルメント変更のブロック
エンタイトルメントファイルをエージェントによる意図しない変更から保護します:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"command": "bash -c 'INPUT=$(cat); FP=$(echo \"$INPUT\" | jq -r \".tool_input.file_path // empty\"); if echo \"$FP\" | grep -qE \"\\.entitlements$\"; then echo \"BLOCKED: Do not modify entitlements files without explicit permission.\" >&2; exit 2; fi'"
}
]
}
}
統合iOS向けフック設定
以下は、すべてのiOSプロジェクトで使用している完全な.claude/settings.jsonです:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"command": "bash -c 'INPUT=$(cat); FP=$(echo \"$INPUT\" | jq -r \".tool_input.file_path // empty\"); if echo \"$FP\" | grep -qE \"\\.(pbxproj|xcworkspace|xib|storyboard|entitlements)$|xcodeproj/|xcworkspace/\"; then echo \"BLOCKED: Do not modify Xcode-managed files. Create Swift files and add manually.\" >&2; exit 2; fi'"
}
],
"PostToolUse": [
{
"matcher": "Edit|Write",
"command": "bash -c 'INPUT=$(cat); FP=$(echo \"$INPUT\" | jq -r \".tool_input.file_path // empty\"); if echo \"$FP\" | grep -qE \"\\.swift$\"; then swiftformat \"$FP\" --quiet 2>/dev/null; fi'"
}
]
}
}
これにより、2つの保証が得られます: 1. エージェントがXcodeプロジェクトファイルを破損できないこと(PreToolUseブロック) 2. エージェントが触れたすべてのSwiftファイルが自動フォーマットされること(PostToolUseフォーマット)
エージェントと相性の良いアーキテクチャパターン
すべてのSwiftアーキテクチャがエージェントと同じように相性が良いわけではありません。以下のパターンが最も良い結果を生むのは、明示的で一貫性があり、トレーニングデータに十分含まれているためです。
@Observable(ObservableObjectは使用禁止)
iOS 26+をターゲットとする場合、@Observableのみを使用してください。これはモダンなパターンであると同時に、エージェントフレンドリーなパターンでもあります:
// CORRECT — @Observable
@Observable
@MainActor
final class TimerManager {
var timeRemaining: TimeInterval = 0
var state: TimerState = .stopped
func start() {
state = .running
// ...
}
}
// In a view:
struct TimerView: View {
@State private var timer = TimerManager()
var body: some View {
Text(timer.timeRemaining, format: .number)
}
}
// WRONG — ObservableObject (deprecated pattern)
class TimerManager: ObservableObject {
@Published var timeRemaining: TimeInterval = 0
@Published var state: TimerState = .stopped
}
// WRONG — @StateObject (deprecated pattern)
struct TimerView: View {
@StateObject private var timer = TimerManager()
}
@Observableがエージェントフレンドリーな理由: パターンがシンプルで(@Publishedアノテーションが不要)、所有権モデルが明確(@StateObjectと@ObservedObjectの使い分けではなく@Stateのみ)、可動部品が少ないためエージェントが生成するバグも少なくなります。
CLAUDE.mdに記載すべき事項: iOS 26をターゲットにしていても、エージェントはトレーニングデータに含まれるObservableObjectパターンに戻ってしまうことがあります。明示的に禁止することでこれを防げます。
NavigationStack(NavigationViewは使用禁止)
// CORRECT
NavigationStack {
List(items) { item in
NavigationLink(value: item) {
ItemRow(item: item)
}
}
.navigationDestination(for: Item.self) { item in
ItemDetailView(item: item)
}
}
// WRONG
NavigationView {
List(items) { item in
NavigationLink(destination: ItemDetailView(item: item)) {
ItemRow(item: item)
}
}
}
NavigationStackはiOS 16+で利用可能であり、新規コードで使用すべき唯一のナビゲーションパターンです。型安全なnavigationDestination(for:)パターンにより、エージェントが不正なナビゲーションリンクを作成するのを防止できます。
SwiftDataによる永続化
SwiftDataモデルは、エージェント支援開発において最もクリーンな永続化パターンです:
@Model
final class GroceryItem {
var name: String
var quantity: Int
var isCompleted: Bool
var category: Category?
var list: GroceryList?
init(name: String, quantity: Int = 1) {
self.name = name
self.quantity = quantity
self.isCompleted = false
}
}
SwiftDataを扱うエージェント向けの重要ルール:
1. @Modelクラスは自動的にObservableとなるため、@Observableを追加しないこと
2. フォームバインディングには@Bindableを使用:@Bindable var item: GroceryItem
3. ビューではリアクティブデータに@Queryを使用:@Query var items: [GroceryItem]
4. ビュー以外のコードではmodelContext.fetch()を使用
5. リレーションの削除には明示的なルールが必要:.cascade、.nullify、.deny
Swift 6.2の並行処理
新規プロジェクトではSwift 6.2の厳密な並行処理をターゲットにしましょう:
// Actor isolation for shared mutable state
@MainActor
@Observable
final class DataManager {
var items: [Item] = []
func loadItems() async throws {
let fetched = try await api.fetchItems()
items = fetched // Safe: @MainActor isolated
}
}
// Sendable conformance for cross-actor transfers
struct Item: Sendable, Identifiable {
let id: UUID
let name: String
let createdAt: Date
}
並行処理に関するエージェントへのガイダンス:
- すべてのビューモデルに@MainActorを付与(データ競合の警告を防止)
- すべての非同期処理にasync/awaitを使用(completionハンドラーは使わない)
- アクター間転送のために値型をSendable準拠にする
- ビューでの非同期初期化にはTask { }を使用
- nonisolatedはパフォーマンス上の必要性を計測した場合のみ使用
Liquid Glassデザインシステム(iOS 26+)
iOS 26でLiquid Glassデザインシステムが導入されました。明示的なガイダンスがあれば、エージェントはうまく対応できます:
// Glass effect on containers
VStack {
// content
}
.glassEffect()
// Glass effect with tint
Button("Action") { }
.glassEffect(.regular.tint(.blue))
// Glass effect on navigation bars (automatic in iOS 26)
NavigationStack {
// content
}
// Navigation bar automatically uses glass material
// Custom glass shapes
RoundedRectangle(cornerRadius: 16)
.fill(.ultraThinMaterial)
.glassEffect()
CLAUDE.mdに記載すべき内容:「セクション背景やカードコンテナには.glassEffect()を使用すること。iOS 26ではナビゲーションバーが自動的にグラスマテリアルを採用します。カスタムマテリアルでグラスエフェクトを手動で再現しないでください — システムモディファイアを使用してください。」
フレームワーク固有のコンテキスト
各Appleフレームワークには、エージェント固有の考慮事項があります。このセクションでは、8つのアプリで使用されているフレームワークについて解説します。
HealthKit
使用アプリ: Return、Water
HealthKitでは、パーミッション処理とプラットフォームガードを慎重に行う必要があります:
// Always check availability and authorization
import HealthKit
@MainActor
@Observable
final class HealthKitManager {
private let store = HKHealthStore()
var isAuthorized = false
func requestAuthorization() async {
guard HKHealthStore.isHealthDataAvailable() else { return }
let types: Set<HKSampleType> = [
HKQuantityType(.dietaryWater),
HKCategoryType(.mindfulSession)
]
do {
try await store.requestAuthorization(toShare: types, read: types)
isAuthorized = true
} catch {
// User denied — do not retry automatically
}
}
}
HealthKitに関するエージェントルール:
- 必ずHKHealthStore.isHealthDataAvailable()でガードすること
- 認可を仮定せず、書き込みのたびに確認すること
- マルチプラットフォームコードでは#if canImport(HealthKit)を使用(tvOSではHealthKitが利用不可)
- HealthKitが提供する以上のヘルスデータをローカルに保存しないこと
- Info.plistにNSHealthShareUsageDescriptionとNSHealthUpdateUsageDescriptionの両方を含めること
SpriteKit
使用アプリ: TappyColor、Starfield Destroyer
SpriteKitのシーングラフモデルには、エージェントへの明示的なガイダンスが必要です:
## SpriteKit Rules
- Scene hierarchy is a tree of SKNodes with zPosition ordering
- Physics bodies use category bitmasks (UInt32) for collision detection
- Node pooling: pre-allocate reusable nodes (bullets, particles)
- Never add nodes directly to the scene — use layer nodes for organization
- Update loop: `update(_ currentTime:)` runs every frame — keep it fast
- Actions: use SKAction sequences for animations, not manual property updates
- Textures: use texture atlases for performance (.atlas directories)
SpriteKitにおけるエージェントの強み: - SKActionのシーケンスやグループの作成 - 物理ボディと接触検出のセットアップ - ゲームステートマシンの実装 - HUDオーバーレイの構築
SpriteKitにおけるエージェントの弱み: - パフォーマンスが重要なゲームループ(エージェントはフレームごとに不要な処理を追加しがち) - 複雑な物理シミュレーション(精度が求められる場合、SKPhysicsBodyよりカスタム物理の方が適切) - パーティクルエフェクトの調整(視覚的な作業であり、反復的なイテレーションが必要)
Metal
使用アプリ: amp97、Water、Starfield Destroyer
Metalは、エージェントが最も苦戦するフレームワークです。GPUプログラミングモデルはCPU側のSwiftとは根本的に異なり、エージェントは視覚的な出力を検証できません。
## Metal Rules
- Shared types between Swift and Metal go in a bridging header (ShaderTypes.h)
- Triple buffer in-flight frames (semaphore with value 3)
- Test shaders on DEVICE, not simulator (Metal behavior differs)
- Compute shaders: threadgroup size must divide evenly into grid size
- Fragment shaders: output color must be in correct color space (sRGB or linear)
- DO NOT optimize shaders without Instruments GPU profiling data
MetalプロジェクトのCLAUDE.mdに含めるべき内容: - Uniforms構造体の定義(SwiftとMSLで共有) - レンダーパイプラインステートのセットアップパターン - バッファインデックスとその用途 - 既存のシェーダーとそれぞれの役割 - 既知の精度問題(halfとfloatの違い)
Live Activities
使用アプリ: Return
Live Activitiesには特定の設定が必要ですが、ドキュメント化すればエージェントはうまく対応できます:
## Live Activities
- ActivityAttributes defined in `TimerActivityAttributes.swift`
- ActivityKit framework: `import ActivityKit`
- Widget extension: `ReturnWidgets/ReturnLiveActivity.swift`
- Start: `Activity<TimerActivityAttributes>.request(attributes:content:)`
- Update: `activity.update(ActivityContent(state:staleDate:))`
- End: `activity.end(ActivityContent(state:staleDate:), dismissalPolicy:)`
- Push token: register for updates via `activity.pushTokenUpdates`
Game Center
使用アプリ: Starfield Destroyer
## Game Center
- Authentication: `GKLocalPlayer.local.authenticateHandler`
- Leaderboards: `GKLeaderboard.submitScore(_:context:player:leaderboardIDs:completionHandler:)`
- Achievements: `GKAchievement.report(_:withCompletionHandler:)` (takes `[GKAchievement]` array)
- Always check `GKLocalPlayer.local.isAuthenticated` before submitting
- Handle authentication failure gracefully (offline play must work)
マルチプラットフォームパターン
Returnは iOS、watchOS、tvOS にまたがるアプリです。エージェントを活用したマルチプラットフォーム開発では、プラットフォーム境界を明示的にドキュメント化する必要があります。
共有コードの構成
Shared/
├── MeditationSession.swift # Data model (all platforms)
├── SessionStore.swift # iCloud sync (all platforms)
└── SessionHistoryView.swift # UI (adapts per platform)
Return/ # iOS-specific
ReturnWatch Watch App/ # watchOS-specific
ReturnTV/ # tvOS-specific
エージェントへのルール:「Shared/ にあるファイルを変更すると、すべてのプラットフォームに影響します。プラットフォーム固有のディレクトリにあるファイルの変更は、そのプラットフォームに限定されます。ファイルを修正する前に、必ずどのディレクトリに属するか確認してください。」
プラットフォーム可用性ガード
// HealthKit: available on iOS and watchOS, not tvOS
#if canImport(HealthKit)
import HealthKit
// HealthKit code here
#endif
// ActivityKit: available on iOS only
#if canImport(ActivityKit)
import ActivityKit
// Live Activity code here
#endif
// WatchKit: available on watchOS only
#if os(watchOS)
import WatchKit
// Watch-specific code here
#endif
エージェントへのガイダンス:「プラットフォーム固有のフレームワークを使用する際は、必ず #if canImport() または #if os() ガードを使用してください。すべてのターゲットでフレームワークが利用可能だと想定してはいけません。」
プラットフォームごとのUI適応
struct SessionHistoryView: View {
@Query var sessions: [MeditationSession]
var body: some View {
List(sessions) { session in
SessionRow(session: session)
}
#if os(tvOS)
.focusable()
#endif
#if os(iOS)
.swipeActions {
Button("Delete", role: .destructive) {
// delete
}
}
#endif
}
}
高度なワークフロー
自律型ビルド・テスト・修正ループ
最も強力なパターンは、エージェントに機能仕様を与え、ビルド→テスト→修正のサイクルを自律的に繰り返させることです。
Implement a countdown timer that:
1. Starts from a user-selected duration (10, 20, or 30 minutes)
2. Shows remaining time with a circular progress indicator
3. Plays a bell sound on completion
4. Logs the session to HealthKit as mindful minutes
Build after each change. Fix all errors. Run tests when the build succeeds.
Continue until all tests pass and the build is clean.
エージェントがコードを書き、MCP でビルドし、構造化されたエラーを読み取り、修正して繰り返します。人間なら5〜10回のビルド・エラー・修正サイクルが必要な機能も、1回の自律ループで完了できます。
うまくいく場面: 明確な受け入れ基準を持つ、定義が明確な機能。
うまくいかない場面: オープンエンドな機能(「見た目をきれいにして」など)、パフォーマンスに敏感なコード、視覚的な確認が必要なもの。
iOS向けサブエージェント委任
Claude Code のサブエージェントシステムはiOSプロジェクトでも活用できます。
Use a subagent to research the best approach for implementing
iCloud key-value store sync for meditation sessions across iOS,
watchOS, and tvOS. Report back with the recommended pattern.
サブエージェントは別のコンテキストウィンドウでドキュメントやコードパターンを調査し、要約を返します。メインセッションがその推奨事項を実装する流れです。これにより、調査でプライマリコンテキストが消費されるのを防げます。
クロスアプリパターンの適用
一貫したパターンを持つ複数のiOSアプリを管理している場合、エージェントがあるアプリのパターンを別のアプリに適用できます。
Look at how Settings.swift works in the Return project
(centralized singleton with validation). Apply the same pattern
to create a Settings.swift for the Water project.
エージェントがソースパターンを読み取り、構造を理解した上で、ターゲットプロジェクトに一貫性のある実装を作成します。
デュアルエージェントレビュー(Claude + Codex)
重要な変更には、異なるモデルファミリーの2つのエージェントを使用しましょう。
- Claude Code が実装を作成
- Codex CLI が別パスでレビュー
# After Claude implements the feature:
codex "Review the changes in the last commit. Focus on Swift 6.2
concurrency correctness, SwiftData relationship integrity,
and potential retain cycles. Report issues only — no praise."
異なるモデルファミリーは異なるクラスのエラーを検出します。特にMetalシェーダーや並行処理パターンでは、微妙なバグが混入しやすいため、このアプローチが有効です。
デュアルレビューで発見でき、シングルレビューでは見逃されるもの:
| 問題の種類 | Claude の強み | Codex の強み |
|---|---|---|
| SwiftData リレーションサイクル | 中程度 | 強い(GPT-4o) |
| @MainActor 分離のギャップ | 強い | 中程度 |
| Metal バッファアライメント | 中程度 | 中程度 |
| リテインサイクルの検出 | 強い(Opus) | 強い(o3) |
| API 非推奨APIの認識 | 強い(より新しい学習データ) | 中程度 |
| 並行処理の競合状態 | 強い | 強い(異なるパターンを検出) |
デュアルレビューの目的は、より多くのバグを見つけることではなく、異なる種類のバグを見つけることです。各モデルファミリーには、パターン認識における固有の失敗モードがあります。
複数アプリへのバッチ操作
フレームワークやパターンの変更が複数のアプリに影響する場合:
# Update @Observable pattern across all projects
for project in BananaList Return Water Reps; do
cd ~/Projects/$project
claude -p "Audit all files for any remaining ObservableObject usage.
Convert to @Observable following the pattern in CLAUDE.md.
Build and test after changes." --dangerously-skip-permissions
done
注意して使用してください。 --dangerously-skip-permissions フラグは非対話モードで必須ですが、すべての安全チェックをバイパスします。.pbxproj ファイルを保護するための PreToolUse フックが設定されていることを必ず確認してください。
実践的なケーススタディ
抽象的なアドバイスは簡単です。ここでは、8つのアプリから得た具体的なシナリオを紹介します。エージェント支援によるiOS開発が実際にどのように機能するか——失敗も含めて——を示すものです。
ケーススタディ1:ReturnへのTVアプリ追加(成功)
タスク: 瞑想タイマーアプリReturnにtvOSターゲットを追加する。ReturnにはすでにiOSとwatchOSバージョンがあり、TVアプリにはSiri Remoteナビゲーション、大画面UI、iOSアプリとの設定同期が必要でした。
エージェントがうまくやったこと:
- 既存のiOS TimerManagerを読み取り、Live ActivitiesとHealthKit(tvOSでは利用不可)を除外したTVTimerManagerを作成
- Siri Remoteのフォーカスナビゲーション用のカスタムボタンスタイルを作成(TVCapsuleButtonStyle、TVCircleButtonStyle)
- ホイールピッカー(Siri Remoteでは使用不可)を+/-ボタンに置き換えるTVStepperコンポーネントを構築
- App Groups(group.com.941apps.Return)による設定同期を実装
- 共有コード全体に#if os(tvOS)ガードを追加
- MCPでplatform=tvOS Simulator,name=Apple TVを使用してビルド・テストを実施
手動で行う必要があったこと: - XcodeでtvOSターゲットを作成(File > New > Target > tvOS App) - 新しいターゲットをXcodeプロジェクトに追加(.pbxprojの変更) - TVターゲットのApp Groupsエンタイトルメントを設定 - 既存のスキームにTVターゲットを追加するか、新しいスキームを作成 - エージェントが作成したすべてのSwiftファイルをTVターゲットに手動で追加 - Siri Remoteナビゲーションを手動でテスト(エージェントはフォーカス動作を評価できません)
結果: 15個の新しいSwiftファイル、完全に機能するTVアプリが、約3時間のエージェント支援作業で実装されました。手動で同等の実装を行った場合、1〜2日かかったでしょう。エージェントがコードの約80%を担当し、Xcode UIの操作と手動テストが必要な20%を私が担当しました。
ケーススタディ2:amp97でのMetalシェーダーデバッグ(部分的な失敗)
タスク: オシロスコープシェーダーにエネルギーベースの強度システムを追加する。ビジュアライゼーションがオーディオエネルギーに合わせて脈動するようにする。
何が起きたか:
1. エージェントはuEnergyユニフォームとHDRトーンマッピングを追加する有効なMetalシェーダー修正を記述
2. コードはエラーなくコンパイルされた
3. デバイス上でビジュアライゼーションが完全に白くなった——強度係数が10倍高すぎた(0.30ではなく3.5)
4. エージェントは白い画面を見ることができないため、フィードバック信号がなかった
5. 私が視覚的に問題を特定し、エージェントに係数を下げるよう依頼
6. エージェントは係数を下げたが、全体的なエネルギーステートマシンが複雑すぎて、別の形でビジュアライザーが壊れた
7. 完全にリバート——2つのコミット(67959edとcda4830)が869d914でリバートされた
教訓: Metalシェーダーは、エージェント支援開発で最も困難な領域です。フィードバックループが断絶しているためです。エージェントは構文(コンパイルが通る)とセマンティクス(型が正しい)を検証できますが、出力(見た目が正しい)を検証できません。視覚的な動作を変更するシェーダー修正には、デバイス上での人間による検証が必須です。
この後CLAUDE.mdに追加した内容: 「係数を極めて慎重にテストせずに、オシロスコープシェーダーのエネルギーステート変更を試みないこと。以前の試みでは係数が10倍高すぎてビジュアライザーが壊れた。」
ケーススタディ3:Banana ListでのSwiftDataマイグレーション(成功)
タスク: データモデルをV1からV2にマイグレーションし、GroceryItemにquantityフィールドを追加し、リレーションシップを持つ新しいCategoryモデルを追加する。
エージェントが行ったこと:
1. 既存のV1モデル定義を読み取り
2. 新しいフィールドとリレーションシップを含むV2モデル定義を作成
3. SchemaMigrationPlanプロトコルに準拠したGroceryMigrationPlanを記述
4. V1toV2マイグレーションステージを実装:デフォルト値quantity: 1とcategory: nilを追加
5. 新しいフィールドをサポートするようにすべてのビューを更新
6. プレビュー用のSampleData.swiftを更新
7. MCPでビルドしてテストを実行——すべてパス
8. マイグレーション専用のユニットテストを作成
成功の鍵: SwiftDataのマイグレーションは、Appleのドキュメントやトレーニングデータに十分に表現されている明確に定義されたプロトコルパターンに従っているため、エージェントは成功しました。CLAUDE.mdにV1モデルが明示的に記述されていたため、エージェントはマイグレーション元を正確に理解できました。
ケーススタディ4:Returnでのicloudセッション同期(複雑さを伴う成功)
タスク: クロスデバイスの瞑想セッションログを実装する。Apple TVやMacで完了したセッションをiPhoneに同期し、HealthKitへの記録を行う。
エージェントが生成したもの:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ tvOS │ │ Mac │ │ Watch │
│ TVTimerMgr │ │ TimerMgr │ │ WatchTimer │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
└───────────────────┼───────────────────┘
│
▼
┌────────────────────────┐
│ SessionStore │
│ (iCloud Key-Value) │
└───────────┬────────────┘
│
▼
┌────────────────────────┐
│ iPhone (on foreground)│
│ → Write to HealthKit │
└────────────────────────┘
エージェントの作業:
1. UUID、日付、継続時間、ソースデバイス、HealthKit同期ステータスを含むMeditationSessionデータモデルを作成
2. iCloud同期用のNSUbiquitousKeyValueStoreを管理するSessionStoreシングルトンを構築
3. マージ競合解決(UUIDベースの重複排除)を実装
4. プラットフォーム固有のアダプテーションを持つSessionHistoryViewを追加(iOSではスワイプで削除、tvOSではフォーカスベース)
5. 他のデバイスからのセッションに対するiPhone側のHealthKit同期を接続
イテレーションが必要だった点: 初期実装では、iPhoneアプリがバックグラウンドで起動するケース(同期のためのフォアグラウンド通知がない)が処理されていませんでした。エージェントには具体的なガイダンスが必要でした:「NSUbiquitousKeyValueStore.didChangeExternallyNotificationを使用して、バックグラウンドKV変更時に同期をトリガーしてください。」このヒントの後、実装は正しくなりました。
教訓: アーキテクチャが明確に記述されていれば、エージェントはマルチプラットフォームのアーキテクチャパターンをうまく処理できます。iCloud同期パターンは単純ではありませんが、エージェントが理解できるAppleの文書化されたパターンに従っています。エッジケース(バックグラウンド同期)には人間のドメイン知識が必要でした。十分に文書化されていない領域だからです。
ケーススタディ5:Starfield DestroyerでのGame Center統合(成功)
タスク: スペースシューターにGame Centerのリーダーボードと実績を追加する。
エージェントがうまくやったこと:
- アプリのエントリポイントでGKLocalPlayer.local.authenticateHandlerを実装
- スコア送信と実績報告のメソッドを持つGameCenterManagerを作成
- すべてのGame Center操作の前に認証状態チェックを追加
- オフラインケースを適切に処理(Game Centerなしでゲームが動作し、再接続時に送信)
- 8機の機体プログレッションシステムに対応する実績定義を作成
手動作業が必要だったこと: - App Store Connectでリーダーボードと実績を作成(Webポータル、エージェントからアクセス不可) - XcodeでGame Centerエンタイトルメントを設定 - サンドボックスGame Centerアカウントでテスト(デバイス上での手動サインインが必要)
エージェントとのプロジェクトライフサイクル
新しいiOSプロジェクトの開始
エージェント支援で新しいプロジェクトを開始する最適なワークフローは以下の通りです。
フェーズ1:人間によるセットアップ(15〜30分) 1. Xcodeプロジェクトを作成(File > New > Project) 2. 署名と機能を設定 3. デプロイメントターゲットとサポートするデスティネーションを設定 4. 必要なエンタイトルメントを追加(HealthKit、Game Centerなど) 5. プロジェクトのアイデンティティとルールを記載した初期CLAUDE.mdを作成
フェーズ2:エージェントによる実装(数時間〜数日) 1. エージェントがデータモデルを作成(SwiftData、Core Data、またはプレーンな構造体) 2. エージェントが文書化されたパターンに従ってビューを構築 3. エージェントがマネージャー/サービスクラスにビジネスロジックを実装 4. エージェントがユニットテストを記述 5. MCPによるビルド・テスト・修正ループ(自律的)
フェーズ3:人間による統合(30〜60分) 1. エージェントが作成したファイルをXcodeターゲットに追加 2. 署名とエンタイトルメントを確認 3. 実機でテスト 4. ビジュアルレイアウトとUXを確認 5. App Store Connectに提出
既存プロジェクトのメンテナンス
確立されたアプリの継続的な開発の場合:
You: Here's the next feature for Return: add a "Focus Score" that
calculates from session duration and consistency. Show it on
the main timer view. Build and test.
Claude: [reads CLAUDE.md, understands patterns]
[reads TimerManager.swift, Settings.swift]
[creates FocusScore.swift — model with calculation logic]
[creates FocusScoreView.swift — display component]
[integrates into ContentView.swift]
[calls build_sim — 1 error: missing import]
[fixes import]
[calls build_sim — success]
[writes FocusScoreTests.swift]
[calls test_sim — all pass]
エージェントの効果は、CLAUDE.mdがプロジェクトの現在の状態をどれだけ正確に反映しているかに比例します。重要な新機能の追加、アーキテクチャパターンの変更、新しいフレームワークの導入時には、CLAUDE.mdを更新しましょう。
エージェントに任せるべきタスクとそうでないタスク
| タスク | エージェント? | 理由 |
|---|---|---|
| 新しいSwiftUIビュー | はい | エージェントは宣言的UIが得意 |
| SwiftDataモデルの変更 | はい | 明確に定義されており、テスト可能 |
| ユニットテスト | はい | 機械的でパターンベース |
| リファクタリング | はい | 体系的で複数ファイルにまたがる |
| ビルドエラーの診断 | はい(MCP経由) | 構造化されたフィードバックループ |
| 新しいXcodeターゲット | いいえ | Xcode UIと.pbxprojの変更が必要 |
| 署名とプロビジョニング | いいえ | ポータルベースでエージェントからアクセス不可 |
| ビジュアルの仕上げ | いいえ | 人間の美的判断が必要 |
| Metalシェーダーの調整 | いいえ | デバイスでのGPUテストが必要 |
| App Store提出 | いいえ | ポータルとXcode Organizerが必要 |
| パフォーマンスプロファイリング | いいえ | Instrumentsが必要 |
| アクセシビリティ監査 | 部分的 | エージェントはラベルを追加できるが、VoiceOverの検証は人間が行う |
エージェント定義の設定
Claude Codeのエージェント定義システム(.claude/agents/)を使用する場合、iOS専用のエージェントを作成しましょう。
---
name: ios-developer
description: iOS development agent with MCP build tools and SwiftUI expertise
tools:
- XcodeBuildMCP
- xcode
---
# iOS Developer Agent
You are an iOS development agent for apps targeting iOS 26+ with SwiftUI.
## Architecture Rules
- @Observable for all view models (NEVER ObservableObject)
- NavigationStack for all navigation (NEVER NavigationView)
- SwiftData for persistence
- Swift 6.2 strict concurrency
- @MainActor on all Observable classes
## Build & Test — Always Use MCP
Prefer MCP tools over raw shell commands for ALL build operations:
- **Build**: `build_sim` / `build_device` (NOT `xcodebuild` via Bash)
- **Test**: `test_sim` / `test_device` (NOT `xcodebuild test` via Bash)
- **Simulators**: `list_sims`, `boot_sim`, `open_sim`
- **Debug**: `debug_attach_sim`, `debug_stack`, `debug_variables`
- **Apple docs**: `DocumentationSearch` (NOT WebSearch for Apple APIs)
- **Swift verification**: `ExecuteSnippet` (NOT `swift` via Bash)
MCP returns structured JSON. Bash returns unstructured text.
## File Management Rules
- NEVER modify .pbxproj, .xcodeproj/, .xcworkspace/, .xib, .storyboard
- Create Swift files in the correct directory
- Report files that need manual addition to Xcode targets
## SwiftData Rules
- @Model classes are automatically Observable — do not add @Observable
- Use @Bindable for form bindings to model properties
- Use @Query in views, modelContext.fetch() elsewhere
- Document relationship delete rules
## When You Get Stuck
- Build errors: use `build_sim` via MCP for structured output
- API questions: use `DocumentationSearch` via Apple MCP
- Swift verification: use `ExecuteSnippet` via Apple MCP
- Never guess — verify with tools
Claude Codeのセッションで@ios-developerを参照することで、このエージェントを使用できます。
エージェント支援によるiOSテストパターン
エージェントは明確なガイダンスがあれば優れたユニットテストを書きます。ここでは、最良の結果を生むパターンを紹介します。
テストファイルの構成
# In CLAUDE.md:
## Test Structure
Tests mirror source structure:
- `ReturnTests/TimerManagerTests.swift` tests `TimerManager.swift`
- `ReturnTests/SettingsTests.swift` tests `Settings.swift`
- `ReturnTests/ConstantsTests.swift` tests `Constants.swift`
Test naming: `test_<what>_<condition>_<expected>`
Example: `test_start_whenStopped_transitionsToRunning`
テストのプロンプト作成
効果的なテストプロンプト:
Write unit tests for TimerManager covering:
1. Initial state is .stopped with timeRemaining == selectedDuration
2. start() transitions state to .running
3. pause() from .running transitions to .paused
4. reset() from any state returns to .stopped with original duration
5. start() from .paused resumes (state becomes .running)
6. Edge case: reset() when already stopped is a no-op
7. Edge case: pause() when already paused is a no-op
Follow the existing test pattern in SettingsTests.swift.
Use setUp() to create a fresh TimerManager for each test.
これが効果的な理由: 番号付きの受け入れ基準がエージェントにチェックリストを提供します。既存のテストファイルを参照することでパターンが確立されます。setUp()の使用を指定することで、テスト状態が絡み合うのを防ぎます。
効果の低いテストプロンプト:
Write tests for TimerManager.
これでは汎用的で浅いテストが生成され、エッジケースを見落とし、プロジェクトのパターンに従わない可能性があります。
非同期テストパターン
タイマーベースおよび非同期コードのテストには以下のパターンを使用します。
// Agent produces this pattern when guided correctly:
final class TimerManagerTests: XCTestCase {
var sut: TimerManager!
@MainActor
override func setUp() {
super.setUp()
sut = TimerManager()
}
@MainActor
func test_start_whenStopped_transitionsToRunning() {
// Given
XCTAssertEqual(sut.state, .stopped)
// When
sut.start()
// Then
XCTAssertEqual(sut.state, .running)
}
@MainActor
func test_timerCountsDown_afterOneSecond() async throws {
// Given
sut.selectedDuration = 10
sut.reset()
sut.start()
// When
try await Task.sleep(for: .seconds(1.1))
// Then
XCTAssertLessThanOrEqual(sut.timeRemaining, 9.0)
}
}
エージェントに繰り返し伝えるべき重要なパターン:
- @MainActorクラスをテストするメソッドには@MainActorを付与する
- Task.sleepや非同期操作を使うテストにはasync throwsを付ける
- 時間ベースのアサーションには許容誤差を設ける(正確に1.0秒ではなく1.1秒)
- テスト分離のためにsetUp() / tearDown()をクリーンに保つ
スナップショットテスト
ビジュアルリグレッションの検出には、swift-snapshot-testingの導入を検討してください。
Add snapshot tests for the main timer view in three states:
1. Stopped (showing full duration)
2. Running (showing countdown)
3. Completed (showing 00:00 with completion state)
Use SnapshotTesting library. Create reference images on first run.
エージェントはスナップショットテストを正しくセットアップできますが、参照画像の確認はできません。最初のスナップショットは開発者自身が確認し、その後はエージェントのテストが将来の変更によるビジュアルリグレッションを検出します。
iOSプロジェクトのコンテキストウィンドウ管理
1Mコンテキストウィンドウ(Opus 4.6)は大容量ですが無限ではありません。iOSプロジェクトには固有のコンテキスト管理の考慮事項があります。
iOSファイルのトークンコスト
| ファイルタイプ | 一般的なサイズ | おおよそのトークン数 |
|---|---|---|
| SwiftUIビュー(シンプル) | 50〜100行 | 500〜1,000 |
| SwiftUIビュー(複雑) | 200〜400行 | 2,000〜4,000 |
| SwiftDataモデル | 30〜80行 | 300〜800 |
| マネージャー/サービスクラス | 100〜300行 | 1,000〜3,000 |
| Metalシェーダー(.metal) | 50〜200行 | 500〜2,000 |
| ユニットテストファイル | 50〜200行 | 500〜2,000 |
| CLAUDE.md | 100〜300行 | 1,000〜3,000 |
| MCPレスポンス(ビルド) | 可変 | 200〜2,000 |
| MCPレスポンス(テスト) | 可変 | 500〜5,000 |
50ファイル規模のプロジェクトの場合: 全ファイルの読み込みで約50,000〜100,000トークンを消費しますが、1Mウィンドウの範囲内に十分収まります。エージェントはプロジェクト全体をコンテキストに保持できます。
100ファイル以上のプロジェクトの場合: 選択的な読み込みが必要になります。エージェントはまずCLAUDE.md(ファイル構造のアノテーション用)を読み、必要に応じて特定のファイルを読み込みます。CLAUDE.mdのファイルアノテーションが重要なのはこのためです — すべてを読み込むことなく、エージェントを適切なファイルに導きます。
大規模プロジェクト向けの戦略
- CLAUDE.mdの詳細なファイルアノテーション — エージェントがファイルマップを読み、関連ファイルに直接アクセスできます
- サブエージェントへの委任 — 探索やリサーチをサブエージェントに任せます(クリーンなコンテキストで要約を返します)
- 焦点を絞ったプロンプト — 「設定を更新して」よりも「SettingsView.swiftに新しいトグルを追加して」の方が効果的です
- セッション境界の設定 — 長いセッションを延長するよりも、無関係な機能には新しいセッションを開始しましょう
/compactの活用 — Claude Codeのコンパクションコマンドで会話を要約し、コンテキストを解放できます
MCPのトークン効率
MCPを推奨する最も強力な理由の一つが、構造化されたJSONレスポンスが生のxcodebuild出力よりもはるかに少ないトークンで済むことです。
| シナリオ | 生のBashトークン数 | MCPトークン数 | 削減率 |
|---|---|---|---|
| ビルド成功 | 3,000〜10,000 | 200〜500 | 85〜95% |
| ビルド失敗(エラー1件) | 3,000〜10,000 | 300〜800 | 90〜92% |
| テスト結果(20テスト) | 2,000〜5,000 | 500〜1,000 | 75〜80% |
| シミュレーター一覧 | 500〜2,000 | 200〜400 | 60〜80% |
10〜20回のビルドサイクルを伴う一般的な開発セッションでは、MCPは生のxcodebuildと比較して30,000〜150,000トークンを節約します。これは実際のコード推論に使えるトークンがそれだけ増えることを意味します。
トラブルシューティング
「build_sim failed — scheme not found」エラー
エージェントがスキーム名を推測しています。対処法:
Use discover_projs and list_schemes to find the correct scheme name
for this project before building.
または、CLAUDE.mdにスキーム名を明示的に記載してください。
## ビルド
プライマリスキーム:`Return`(iOS)
Watchスキーム:`ReturnWatch`(watchOS)
TVスキーム:`ReturnTV`(tvOS)
「xcrun mcpbridge — command not found」
Xcode 26.3以降が必要です。xcodebuild -versionで確認してください。Xcode 26.3以降がインストール済みでもコマンドが失敗する場合:
# Ensure Xcode command line tools are selected
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
# Verify
xcrun mcpbridge --help
「MCPツールがClaude Codeに表示されない」
セッション中に登録したMCPツールは、再起動するまで表示されない場合があります。Claude Codeを終了し、新しいセッションを開始してください:
# Exit current session (Ctrl+C or /exit)
# Start fresh
claude
確認方法:
You: List all available MCP tools from XcodeBuildMCP.
「エージェントがMCPではなくBash経由でxcodebuildを使い続ける」
エージェントがTool Search経由でMCPツールを検出できていません。2つの対処法があります:
- CLAUDE.mdに明示的なガイダンスを追加する(エージェントにMCPの使用を教えるを参照)
- 直接プロンプトで指示する: 「Bash経由のxcodebuildではなく、build_sim MCPツールを使用してください」
「ビルドは成功するがエージェントが失敗と報告する」
XcodeBuildMCPはxcodebuildの出力を解析します。ビルドがエラーのように見える警告を出す場合(廃止警告でよく発生)、エージェントが結果を誤解することがあります。MCPレスポンスの実際のステータスフィールドを確認してください。
「シミュレータがブート中にハングする」
すべてのシミュレータを終了して再起動します:
xcrun simctl shutdown all
xcrun simctl boot "iPhone 16 Pro"
または、エージェントに依頼することもできます:
Shut down all simulators, then boot a fresh iPhone 16 Pro.
「CLAUDE.mdのルールにもかかわらず、エージェントが.pbxprojを変更しようとした」
CLAUDE.mdのルールは提案にすぎません。フックが強制力を持ちます。.pbxproj書き込みをブロックするPreToolUseフックを設定していない場合、エージェントはいずれ変更を試みます。フックをインストールしてください:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"command": "bash -c 'INPUT=$(cat); FP=$(echo \"$INPUT\" | jq -r \".tool_input.file_path // empty\"); if echo \"$FP\" | grep -qE \"\\.(pbxproj|xcworkspace|xib|storyboard)$|xcodeproj/|xcworkspace/\"; then echo \"BLOCKED: Do not modify Xcode project files.\" >&2; exit 2; fi'"
}
]
}
}
ルールは「やめてください」と言います。フックは「やらせません」と言います。
FAQ
どのエージェントランタイムから始めるべきですか?
Claude Code CLIとXcodeBuildMCPの組み合わせがおすすめです。MCP統合が最も深く、フックシステムが最も成熟しており、1Mコンテキストウィンドウ(Opus 4.6)でiOSプロジェクト全体をワーキングメモリに保持できます。まずここから始めて、ワークフローが成熟するにつれてCodexをレビュー用に、Xcodeネイティブエージェントを簡易インライン編集用に追加していくとよいでしょう。
両方のMCPサーバーが必要ですか?
ほとんどの開発者にとって、XcodeBuildMCPだけでニーズの90%をカバーできます(ビルド、テスト、シミュレータ、デバッグ)。ドキュメント検索、Swift REPL検証、またはSwiftUIプレビューレンダリングが必要な場合は、AppleのXcode MCPを追加してください。後からいつでも追加可能です。2つのサーバーは独立して動作します。
エージェントはXcodeプロジェクトをゼロから作成できますか?
XcodeBuildMCPには、新しいXcodeプロジェクトを雛形として生成するcreate_projectツールがあります。ただし、本番アプリの場合は、Xcodeでプロジェクトを作成し(署名、ケイパビリティ、ターゲット設定を正確に行うため)、その後のコード実装にエージェントを使用することをおすすめします。Xcodeの新規プロジェクトウィザードに費やす5分が、エージェント生成のプロジェクト設定に関する数時間のトラブルを防いでくれます。
エージェントはSwift Package Managerの依存関係をどう扱いますか?
上手く処理できます。Package.swiftは標準的なSwiftファイルであり、エージェントが確実に読み書きできます。依存関係の追加、バージョン範囲の更新、ターゲットの設定はすべて正常に動作します。制限があるのは.xcodeproj ベースの依存関係管理(Xcodeのパッケージ解決UI)です。これはXcodeが管理するものであり、エージェントが編集すべきではありません。
エージェントはApp Storeに提出できますか?
できません。App Store提出にはXcodeのOrganizer、プロビジョニングプロファイル、スクリーンショット、メタデータ、App Store Connectポータルが関わります。これらはいずれもMCPやコマンドラインツール経由でエージェントが有意義に操作できるものではありません。エージェントが担当するのはアーカイブまでのすべて(実装、テスト、バグ修正、ドキュメント作成)です。提出の最後のステップは人間が行います。
ただし、App Storeのメタデータ作成にはエージェントが役立ちます。最新の変更に基づいて、アプリの説明文、キーワード、最新情報テキストの作成をエージェントに依頼できます。これはエージェントが得意とするテキスト生成の作業です。
エージェント支援のiOS開発でシークレットとAPIキーをどう扱えばよいですか?
シークレットは絶対にコミットしないでください。バックエンドAPIに接続するiOSアプリの場合:
- 環境固有の設定には
.xcconfigファイルを使用する .xcconfigファイルを.gitignoreに追加するInfo.plistのビルド設定経由で設定値を参照する- 必要なシークレットをCLAUDE.mdに記載する(実際の値は含めない)
## Configuration
APIのベースURLとキーは`Config.xcconfig`(コミット対象外)にあります。
必要なキー:
- `API_BASE_URL` — バックエンドサーバーURL
- `API_KEY` — 認証トークン
`Config.xcconfig.template`から`Config.xcconfig`を作成してください。
エージェントはキーの存在と使用場所を把握していますが、実際の値を見ることはありません。
SwiftUIのアニメーションはエージェントに書けますか?
構文的にはアニメーションコードを上手く書けますが、結果を視覚的に検証することはできません。シンプルなアニメーション(.animation(.spring())、.transition(.slide)、withAnimation { })は正しい結果を生成します。精密なタイミングを伴う複雑なマルチステップアニメーションは、エージェントにはできない視覚的な反復調整が必要です。
効果的: 「タイマーが状態遷移する際にスプリングアニメーションを追加してください。」
非効果的: 「タイマーのアニメーションを心地よくしてください。」(主観的であり、視覚的な調整が必要です。)
エージェントはエラーハンドリングパターンをどう扱いますか?
非常に上手く処理できます。エージェントはSwiftのdo/catch、Result、async throwsパターンを理解しています:
Implement error handling for the HealthKit authorization flow:
1. Check HKHealthStore.isHealthDataAvailable() — show alert if not
2. Request authorization — handle denial gracefully
3. On write failure — retry once, then show error
4. All errors should be user-facing with localized descriptions
エージェントは適切なユーザー向けメッセージを含む構造化されたエラーハンドリングを生成します。ただし、エラーを過剰にハンドリングする傾向があり(伝播すべき例外をキャッチするなど)、catchブロックのレビューが必要です。
アクセシビリティの実装にエージェントを使えますか?
部分的に可能です。エージェントはアクセシビリティラベル、ヒント、トレイトを正しく追加できます:
Add accessibility labels to all interactive elements in TimerView:
- Timer display: current time remaining
- Start/Pause button: current state and action
- Reset button: "Reset timer"
- Duration picker: selected duration
エージェントにできないこと:VoiceOverのナビゲーション順序が正しいかの検証、Dynamic Typeスケーリングのテスト、カラーコントラスト比の評価。検証にはXcodeのAccessibility Inspectorを使用してください。
エージェントはCore Dataのマイグレーション(SwiftDataを使用していない場合)をどう扱いますか?
エージェントはCore Dataのマイグレーションマッピングとモデルバージョンを記述できますが、手動のXcode操作(新しいモデルバージョンの作成、現在のバージョンの選択)は自動化できません。SwiftDataではなくCore Dataを使用している場合は、CLAUDE.mdにモデルバージョン履歴を記載してください:
## Core Data Modelのバージョン
- V1: 初期版(GroceryList、GroceryItem)
- V2: Categoryモデルを追加(現行)
- マイグレーション: V1→V2は軽量自動マイグレーション
エージェントはSwiftUIプレビューをどのように扱いますか?
2つの方法があります:
1. Apple Xcode MCPのRenderPreviewツールはプレビューをヘッドレスでレンダリングし、結果を返します。プレビューがコンパイルされエラーなくレンダリングされることは確認できますが、視覚的な正確性の評価はできません。
2. ビルドベースの検証では、build_simによってプレビュープロバイダーがコンパイルされることを確認します。プレビューが実行時にクラッシュしてもビルド自体は成功します。クラッシュはXcodeがプレビューをレンダリングしようとした時にのみ発生します。
プレビューの視覚的な確認には、Xcodeを開いておく必要があります。
visionOSとApple Vision Proについてはどうですか?
同じパターンが適用されます。XcodeBuildMCPはvisionOSシミュレータをサポートしており、アーキテクチャパターン(@Observable、NavigationStack、SwiftData)も同一です。RealityKit固有のコード(3Dコンテンツ、イマーシブスペース、ハンドトラッキング)はMetalと同じ制約に従います。エージェントは正しいコードを書けますが、空間的な出力の検証はできません。
プロジェクトがどれくらい大きくなるとエージェントは苦戦しますか?
コンテキストウィンドウのサイズが制限要因となります。Opus 4.6の1Mトークンウィンドウにより、Claude Codeはアクティブな作業メモリに約50〜70のSwiftファイルを同時に保持できます。より大規模なプロジェクトでは、ファイル検索と選択的読み込みを使用してコードベースのサブセットを扱います。100以上のファイルがあるプロジェクトでも問題なく動作します。すべてをコンテキストに保持するのではなく、必要に応じてファイルを読み込むだけです。
実際の限界はファイル数ではなく、コードベースの一貫性にあります。詳細なCLAUDE.mdを備えた適切にドキュメント化された200ファイルのプロジェクトの方が、ドキュメントのない30ファイルのプロジェクトよりも良い結果を生み出します。
エージェントでiOS開発を行うにはSwiftの知識が必要ですか?
エージェントの出力をレビューし、アーキテクチャ上の判断を下せる必要があります。すべての行を自分で書く必要はありませんが、エージェントが誤った選択をした時にそれを見抜けるだけのSwiftの理解が求められます。特に並行処理、メモリ管理、フレームワーク固有のパターンについてはなおさらです。エージェントは既存のスキルを10倍に増幅するものであり、スキルの代替ではありません。
エージェントはSwiftファイルのマージコンフリクトをどう扱いますか?
エージェントはSwiftソースファイルのマージコンフリクトを確実に解決できます。標準的なコンフリクトマーカー(<<<<<<<、=======、>>>>>>>)はすべてのエージェントランタイムで十分に理解されています。ただし、.pbxprojファイルのマージコンフリクトは手動で解決する必要があります。エージェントに.pbxprojのコンフリクト解決を依頼しないでください。
iOS開発でエージェントを実行するコストはどれくらいですか?
AnthropicのMaxプラン(Opus 4.6、1Mコンテキスト)では、典型的なiOS開発セッションは30〜120分で、200K〜800Kトークンを処理します。MCPのツール呼び出しによるオーバーヘッドはわずかです(構造化されたJSONレスポンスは、生のビルド出力と比較してトークン効率が高いです)。コストは他のコードベースでClaude Codeを実行する場合と同等であり、iOS開発がWeb開発より大幅に高いということも安いということもありません。
UIKitプロジェクトでエージェントを使えますか?
はい、ただしSwiftUIの方がエージェントの効果は高くなります。UIKitはボイラープレートが多く、宣言的な構造が少なく、エージェントが編集できないInterface Builderファイルを含むことが多いためです。UIKitプロジェクトの場合は、モデル層やビジネスロジックにエージェントを活用し、UIは手動で対応するか、段階的にSwiftUIへ移行することを検討してください。
エージェントはローカライゼーションをどう扱いますか?
エージェントは.xcstrings(Xcode文字列カタログ)ファイルを効果的に作成・編集できます。新しいローカライゼーションキーの追加、翻訳の提供、言語間の一貫性維持が可能です。.xcstringsファイルの構造化されたJSONフォーマットはエージェントとの相性が良好です。.stringsファイル(レガシーフォーマット)についても、キーバリュー形式がシンプルなため問題なく対応できます。
iOS開発でよくあるエージェントの間違い(とその防止策)
8つのiOSプロジェクトにわたる数千回のエージェントとのやり取りで繰り返し観察されたエラーをまとめました。それぞれに防止策があります。
間違い1: Observableパターンの混在
発生する問題: エージェントがあるファイルでは@Observableを使い、別のファイルではObservableObjectを使ったり、@Modelクラス(すでにObservable)に@Observableを付けてしまいます。
防止策: CLAUDE.mdに明示的なルールを記載します:
- NEVER use ObservableObject — use @Observable
- NEVER add @Observable to @Model classes (already Observable)
- NEVER use @StateObject — use @State with @Observable
- NEVER use @ObservedObject — access @Observable properties directly
間違い2: クロージャでのリテインサイクル
発生する問題: エージェントがselfを強参照でキャプチャするクロージャを作成してしまいます。特にTimer.publish、NotificationCenter、完了ハンドラーで発生しがちです。
防止策: CLAUDE.mdにクロージャパターンを含めます:
## Closure Pattern
- Timer callbacks: use `[weak self]` and guard
- NotificationCenter observers: store in `Set<AnyCancellable>` and use `[weak self]`
- Completion handlers: use `[weak self]` for any closure stored beyond the call site
間違い3: @MainActorの要件を無視
発生する問題: エージェントが@MainActorアイソレーションなしで@Observableクラスを作成し、Swift 6.2の並行処理警告やUIアップデートがメインスレッド外で発生した際の実行時クラッシュを引き起こします。
防止策:
## Concurrency Rule
ALL @Observable classes MUST be @MainActor:
```swift
@Observable
@MainActor
final class SomeManager { }
### 間違い4: NavigationLinkでのDestination Closureの使用
**発生する問題:** エージェントが非推奨の`NavigationLink(destination:label:)`を使用し、型安全な`NavigationLink(value:)` + `.navigationDestination(for:)`パターンを使いません。
**防止策:**
```markdown
## Navigation Pattern
ALWAYS use value-based navigation:
```swift
NavigationLink(value: item) { ItemRow(item: item) }
.navigationDestination(for: Item.self) { ItemDetailView(item: $0) }
NEVER use: NavigationLink(destination: ItemDetailView(item: item)) { }
### 間違い5: シミュレータ名のハードコーディング
**発生する問題:** エージェントが特定のシミュレータ名("iPhone 16 Pro"など)をビルドコマンドに直接書き込み、お使いのシステムに存在しない可能性があります。
**防止策:** MCPがこれを処理します。`list_sims`で利用可能なシミュレータを検出できます。CLAUDE.mdには以下を記載します:
```markdown
## Simulators
Do NOT hardcode simulator names. Use `list_sims` MCP tool to discover
available devices, then `boot_sim` with the discovered device ID.
間違い6: 誤ったディレクトリへのファイル作成
発生する問題: エージェントが新しいビューファイルをViews/サブディレクトリではなくプロジェクトルートに作成したり、モデルを間違ったグループに配置してしまいます。
防止策: CLAUDE.mdのファイル構成アノテーションが配置をガイドします。さらに以下を追加します:
## File Placement Rules
- Views → `AppName/Views/`
- Models → `AppName/Models/`
- Managers → `AppName/Managers/`
- Extensions → `AppName/Extensions/`
- Tests → `AppNameTests/`
間違い7: プラットフォーム可用性の未対応
発生する問題: エージェントがHealthKitをtvOS(HealthKitが利用不可)向けにもコンパイルされる共有コードで使用したり、ActivityKitをwatchOSコードで使用してしまいます。
防止策:
## Platform Guards
- HealthKit: `#if canImport(HealthKit)` (unavailable on tvOS)
- ActivityKit: `#if canImport(ActivityKit)` (iOS only)
- WatchKit: `#if os(watchOS)`
- UIKit haptics: `#if os(iOS)` (unavailable on tvOS, watchOS uses WKHaptic)
間違い8: シンプルな機能の過剰設計
発生する問題: 本来20行のユーティリティ関数で済むものに対して、エージェントがプロトコル、プロトコル拡張、具象実装、ファクトリ、DIコンテナを作成してしまいます。
防止策: シンプルさの原則を含めます:
## Architecture Principle
Prefer the simplest solution that handles the requirements.
- Direct implementation over protocol abstraction (unless you have 2+ conforming types)
- Concrete types over generics (unless reuse is proven)
- Extensions on existing types over new wrapper types
率直な評価
8つのiOSアプリをAIエージェントとともに出荷した結果をまとめます。
エージェントが変革したもの: 実装速度です。数日かかっていたことが数時間で完了するようになりました。SwiftUIビュー、SwiftDataモデル、ユニットテスト、リファクタリング。これらは今やエージェントが主に生成し、人間がレビューする形になっています。
エージェントが変革しなかったもの: アーキテクチャの意思決定、ビジュアルデザイン、パフォーマンス最適化、App Storeへの提出。これらは依然として人間が主導しています。
生産性の向上は実在しますが、限界があります。 適切にドキュメント化され、MCPとフック設定が整ったプロジェクトでは3〜5倍の生産性向上と見積もっています。ドキュメントもフックもないプロジェクトでは1.5〜2倍程度でしょう。エージェントがビルドではなく推測に時間を費やしてしまうためです。
投資対効果が高いもの: CLAUDE.md、フック、MCPの設定に費やす時間です。セットアップに1時間かければ、エージェントの間違いを修正する何時間もの時間を節約できます。設定こそがプロダクトであり、エージェントは実行エンジンなのです。
驚いたこと: MCPサーバーがいかにダイナミクスを変えたかということです。MCP以前、エージェントはSwiftを理解する高機能なテキストエディタに過ぎませんでした。MCP以後、エージェントはコードを書き、ビルドし、テストし、デバッグし、イテレーションする開発パートナーとなりました。構造化されたフィードバックループこそが、コードを書くエージェントとコードを出荷するエージェントの違いです。
過去の自分に伝えたいこと: 最小のアプリ(Reps、14ファイル)から始め、MCPとフックのセットアップを正しく整え、詳細なCLAUDE.mdを書き、そのパターンをより大きなプロジェクトへ展開すること。63ファイルのマルチプラットフォームアプリから始めてはいけません。インフラへの投資はプロジェクトの規模に関係なく同じです。小さなプロジェクトで一度やれば、あとはすべてにコピーするだけです。
今後の展望: Xcode 26.3のネイティブエージェント統合は、終わりではなく始まりです。AppleがMCPサポートを出荷したということは、ツールチェインがエージェントファーストの開発へ向かっていることを意味します。エージェントに適したプロジェクト構造(整備されたCLAUDE.mdファイル、テスト可能なアーキテクチャ、自動化されたフック)に今投資する開発者は、ツールの改善とともにその投資を複利で回収できるでしょう。
クイックリファレンスカード
インストール(初回セットアップ)
# XcodeBuildMCP (59 tools)
claude mcp add XcodeBuildMCP -s user \
-e XCODEBUILDMCP_SENTRY_DISABLED=true \
-- npx -y xcodebuildmcp@latest mcp
# Apple Xcode MCP (20 tools)
claude mcp add --transport stdio xcode -s user -- xcrun mcpbridge
# Codex MCP setup
codex mcp add xcode -- xcrun mcpbridge
# Verify
claude mcp list
CLAUDE.md の必須セクション
1. Project identity (bundle ID, target OS, architecture)
2. File structure with annotations
3. Build and test commands
4. Key patterns and rules
5. Prohibitions (NEVER touch .pbxproj)
6. Framework-specific context
必須フック
{
"PreToolUse": [{ "matcher": "Edit|Write", "command": "block .pbxproj" }],
"PostToolUse": [{ "matcher": "Edit|Write", "command": "swiftformat" }]
}
アーキテクチャルール
@Observable (not ObservableObject)
NavigationStack (not NavigationView)
@State (not @StateObject)
SwiftData @Model (not Core Data)
async/await (not completion handlers)
@MainActor (on all Observable classes)
.glassEffect() (Liquid Glass, iOS 26+)
MCP ツールの優先順位
Build: build_sim (not xcodebuild via Bash)
Test: test_sim (not xcodebuild test via Bash)
Sim: list_sims/boot_sim (not xcrun simctl via Bash)
Docs: DocumentationSearch (not WebSearch)
REPL: ExecuteSnippet (not swift via Bash)
変更履歴
| 日付 | 変更内容 |
|---|---|
| 2026-04-13 | 初版公開。8アプリ、3ランタイム、MCP セットアップ、CLAUDE.md パターン、フック、ケーススタディを収録。 |
参考文献
-
XcodeBuildMCP はデフォルトで Sentry テレメトリを含んでいます。プロジェクトのプライバシードキュメントに送信内容の詳細が記載されており、エラーメッセージ、スタックトレース、場合によってはファイルパスが含まれます。
XCODEBUILDMCP_SENTRY_DISABLED=true環境変数で完全にオプトアウトできます。 ↩ -
Anthropic、「Model Context Protocol Specification」、modelcontextprotocol.io/specification。MCP 仕様では、XcodeBuildMCP と Apple の Xcode MCP の両方が実装する JSON-RPC トランスポート、ツールディスカバリー、リソースプロトコルが定義されています。 ↩
-
XcodeBuildMCP、github.com/getsentry/XcodeBuildMCP。Sentry がメンテナンスするオープンソースプロジェクトです。シミュレーター、実機、デバッグ、UI 自動化ワークフローにわたる 59 のツールを提供しています。セマンティックバージョニングと変更履歴を採用しています。 ↩
-
Apple は Xcode 26.3 のインテリジェント開発ツールの一環として Xcode MCP サーバーを導入し、AI コーディングアシスタントと Xcode ツールチェーンの間のインターフェースレイヤーとして MCP を位置づけました。公式ドキュメントは Xcode Release Notes をご覧ください。 ↩
-
Rudrank Riyam、「Exploring Xcode Using MCP Tools」、rudrank.com/exploring-xcode-using-mcp-tools-cursor-external-clients、2026年。Apple の MCP ツール数、XPC 依存関係、ドキュメント検索機能についての独立した検証です。 ↩
-
Jimenez, C.E., Yang, J., Wettig, A., et al.、「SWE-bench: Can Language Models Resolve Real-World GitHub Issues?」ICLR 2024。arxiv.org/abs/2310.06770。構造化されたツールアクセスを持つエージェントは、非構造化シェルコマンドに限定されたエージェントを大幅に上回る性能を示しました。この知見は、エージェントの有効性における構造化 MCP インターフェースの有効性を裏付けるものです。 ↩
-
Claude Code CLI ドキュメント、code.claude.com。フックシステム、MCP 設定、サブエージェント委任、エージェント定義について記載されています。 ↩
-
SwiftFormat、github.com/nicklockwood/SwiftFormat。PostToolUse フックで一貫したコードスタイルを維持するために使用される Swift フォーマッターです。 ↩
-
XcodeBuildMCP 公式サイト、xcodebuildmcp.com。CLI の出力例を通じて 59 の MCP ツールを確認できます。ツールカテゴリは、シミュレーター、実機、デバッグ、UI 自動化です。Homebrew または npx でインストール可能です。 ↩
-
Swiftjective-C、「Agentic Coding in Xcode 26.3 with Claude Code and Codex」、swiftjectivec.com、2026年2月。Xcode 26.3 がネイティブの Claude Agent と Codex ランタイムサポートを Settings > Intelligence 経由で提供していることを確認しています。
xcrun mcpbridgeで 20 の MCP ツールが公開されています。 ↩ -
Blake Crosley、「Two MCP Servers Made Claude Code an iOS Build System」、blakecrosley.com/blog/xcode-mcp-claude-code、2026年2月。同著者の iOS 開発ワークフローにおけるセットアップ手順と実際の成果を紹介しています。 ↩