blake@xcode:~/Projects$ cat ios-agent-development.md

AIエージェントで作るiOSアプリ開発:実践者のためのガイド

# AIエージェントを活用してiOSアプリを高速に開発しましょう。Claude Code、Codex CLI、Xcode 26.3ネイティブ対応、MCP、CLAUDE.mdパターン、フック、8つのアプリから得た学びを解説します。

words: 2247 read_time: 63m updated: 2026-04-28 21:51
$ less ios-agent-development.md

TL;DR: 現在、3つのエージェントランタイムがiOS向けのコードを提供しています。MCPを備えたClaude Code CLI、MCPを備えたCodex CLI、そしてXcode 26.3のネイティブIntelligenceエージェントです。2つのMCPサーバー(59個のツールを持つXcodeBuildMCPとAppleのxcrun mcpbridgeの20ツール)により、エージェントはビルド、テスト、シミュレーター、デバッグへの構造化されたアクセスを得られます。本ガイドでは、合計293個のSwiftファイルからなる8つのプロダクションiOSアプリから得られた、実際のCLAUDE.mdパターン、フックの設定、そして何が機能し何が壊れるかについての率直な評価を取り上げます。17 エージェントはSwiftUIビュー、SwiftDataモデル、リファクタリング、ビルドエラーの診断において優れた力を発揮します。一方で、.pbxprojの変更、コード署名、ビジュアルデバッグでは失敗します。「エージェントがSwiftを書く」ことと「エージェントがiOSアプリをリリースする」ことの間にあるギャップは、プロンプトではなく設定によって埋められるのです。

私はAIコーディングエージェントを使って8つのiOSアプリを構築してきました。プロトタイプではありません。HealthKit統合、Metalシェーダー、SpriteKit物理演算、iCloud同期、Live Activities、Game Centerリーダーボード、そしてiOS、watchOS、tvOSにまたがるマルチプラットフォームターゲットを備え、App Storeに公開されているアプリです。これらのアプリのSwiftコードはすべて、エージェントが書いて私がレビューしたか、私が書いてエージェントがリファクタリングしたかのいずれかです。私の見積もりでは、エージェントが行レベルの記述の大半を担当し、私はレビュー、スコープ、そして人間の判断が必要な部分(ビジュアルの仕上げ、署名、パフォーマンスチューニング、App Store提出)を担当しました。

このガイドは、私が始めたときに存在してほしかったリファレンスです。フルスタックを網羅しています。どのエージェントランタイムを使うか、構造化されたビルドアクセスのためにMCPサーバーをどう設定するか、CLAUDE.mdに何を書くか、エージェントがXcodeプロジェクトを破壊するのを防ぐにはどのフックを使うか、そして最も重要な点として、エージェントが失敗してあなたが舵を取らなければならないのはどこか、です。

重要なポイント

AIエージェントを初めて使うiOS開発者向け:

  • Claude Code CLI + XcodeBuildMCPから始めましょう。 これは最も成熟したランタイムで、最も深いMCPツールカバレッジを持ちます。2つのコマンドをインストールし、プロジェクトにCLAUDE.mdを追加すれば、エージェントはあなたがエラーメッセージをコピーすることなくビルド、テスト、デバッグできます。
  • エージェントに.pbxprojを変更させてはいけません。 これは唯一にして最重要のルールです。.pbxproj.xcodeproj/への書き込みをブロックするPreToolUseフックは、何時間もの復旧作業を節約してくれるでしょう。
  • CLAUDE.mdはエージェントのオンボーディングドキュメントです。 ここに費やす時間は、プロジェクトに触れるすべてのエージェントセッションで報われます。

ワークフローにiOSを追加する経験豊富なエージェントユーザー向け:

  • MCPはiOSビルドループを変革します。 MCP以前は、エージェントはSwiftを書けてもコンパイルされたかを検証できませんでした。XcodeBuildMCPを使えば、エージェントはコードを書き、ビルドし、構造化されたエラーを読み、修正し、テストを実行する。これらをすべて自律的に行えます。
  • 3つのランタイムは異なるニーズに対応します。 深いエージェント駆動セッションにはClaude Code CLI、ヘッドレスのバッチ作業にはCodex CLI、IDEを離れずに素早くインライン修正するにはXcode 26.3ネイティブエージェントです。
  • フックインフラはそのまま使えます。 既存のPostToolUseフォーマッタ、PreToolUseブロッカー、テストランナーフックは、わずかなパス調整でiOSプロジェクトでも同じように機能します。

AI支援iOS開発を評価するチームリード向け:

  • エージェントの効果はプロジェクトのサイズではなく、ドキュメンテーションに比例します。 詳細なCLAUDE.mdを持つ63ファイルのアプリは、CLAUDE.mdのない14ファイルのアプリよりも優れたエージェント出力を生み出します。
  • .pbxprojの境界は譲れない一線です。 エージェントはXcodeプロジェクトファイルを確実に編集できません。ワークフローはXcodeターゲットへの手動ファイル追加を考慮する必要があります。
  • 率直なROI:十分にドキュメント化されたプロジェクトでは、エージェントが実装の大部分を担当します。 これは、エージェント支援作業3時間でリリースされた15ファイルのTVアプリで明らかです(後述のケーススタディ参照)。残りの作業——ビジュアルの仕上げ、署名、パフォーマンスチューニング、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 フレームワーク固有のコンテキスト高度なワークフロー
両方に習熟 高度なワークフローフックマルチプラットフォームパターン ランタイム比較ポートフォリオ

目次

  1. ポートフォリオ:8つのアプリ、293ファイル
  2. 前提条件
  3. iOS向け3つのエージェントランタイム
  4. MCPセットアップ:完全な設定
  5. iOSプロジェクト向けCLAUDE.mdパターン
  6. 初めてのエージェントセッション
  7. iOSでエージェントが得意なこと
  8. iOSでエージェントが苦手なこと
  9. iOS開発のためのフック
  10. エージェントとうまく機能するアーキテクチャパターン
  11. フレームワーク固有のコンテキスト
  12. マルチプラットフォームパターン
  13. 高度なワークフロー
  14. 実際のケーススタディ
  15. エージェントによるプロジェクトライフサイクル
  16. エージェント定義の構成
  17. エージェント支援iOS向けテストパターン
  18. iOSプロジェクトのコンテキストウィンドウ管理
  19. トラブルシューティング
  20. iOSにおけるエージェントのよくある間違い
  21. 率直なROI
  22. FAQ
  23. クイックリファレンスカード
  24. 参考文献

関連リソース

トピック リソース
Xcode向けMCPセットアップ(短いブログ記事) 2つのMCPサーバーがClaude CodeをiOSビルドシステムに変えた
Claude Code CLI完全リファレンス Claude Code CLI:完全ガイド
Codex CLIリファレンス Codex CLI:完全ガイド
フックシステム詳説 Clawの解剖:オーケストレーション層としての84個のフック
エージェントアーキテクチャパターン エージェントアーキテクチャガイド

ポートフォリオ: 8つのアプリ、293ファイル

設定の詳細に入る前に、本ガイドの基となった実例を紹介します。これらはおもちゃのプロジェクトではありません。Appleの5つのフレームワーク、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 禅の瞑想タイマー — 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ファイル)は、ファイル注釈、アーキテクチャ図、明示的なパターンを記載した詳細なCLAUDE.mdを持っているため、amp97(41ファイル)よりもエージェントの出力品質が高くなります。amp97のMetalシェーダーは、ドキュメントの品質に関係なく、本質的にエージェントが推論しにくいものです。


前提条件

iOS開発用のエージェントランタイムをセットアップする前に確認すべき項目です:

App Store Connectの締切: 2026-04-28以降、App Store Connectへのアプリアップロードは、iOS 26、iPadOS 26、tvOS 26、visionOS 26、watchOS 26向けのSDKsを使用して、Xcode 26以降でビルドされている必要があります。12(macOSの提出はこの要件の対象外です。)チームがまだXcode 16.xを使用している場合、本ガイドのエージェント支援ツールチェーンが強制要因として機能します。いずれにせよ、以下のMCPサーバーはXcode 26.3+なしでは動作しません。

必須: - macOS 15+(Sequoia)またはmacOS Tahoe - Xcode 26.3+ がインストールされ設定済み(xcrun mcpbridgeの下限)。Xcode 26.4+を推奨します。Swift Testingの画像添付、recorded-issue重要度、クラッシュログ付きUIテストクラッシュ警告、String Catalogエディターの改善などが利用できます。13 Xcode 26.4.1(2026-04-16、build 17E202)が最新の安定版です — バグ修正のみ。14 - 少なくとも1つのiOS Simulatorランタイムがインストール済み - Anthropic APIアカウント(Claude Code用)またはOpenAIアカウント(Codex用)

推奨: - SwiftFormatをインストール(brew install swiftformat) — format-on-saveフックで使用 - SwiftLintをインストール(brew install swiftlint) — オプションですがスタイル統一に役立ちます - ターミナルへの慣れ — 3つのランタイムすべてがコマンドラインから操作されるか、コマンドラインと統合されます

Xcodeインストールの確認:

# Check Xcode version
xcodebuild -version
# Expected: Xcode 26.3 or later (26.4+ recommended)

# 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つのエージェントランタイム

iOSのコードを書き、ビルドし、テストできる3つの異なるランタイムがあります。これらは互換性のあるものではなく、それぞれ異なる強み、異なる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

代替手段 — xcodebuildmcp init 自動インストーラー(v2.1.0+、2026-02-23):

手動でのMCP設定を省略したい場合、XcodeBuildMCP v2.1.0+にはinitサブコマンドが用意されており、Claude Code、Cursor、またはCodexを自動検出してエージェントスキルとMCP設定を1ステップでインストールできます。

xcodebuildmcp init
# Or without a global install:
npx -y xcodebuildmcp@latest init

フラグ:--print(非対応クライアント向けに設定をstdoutに書き出す)、--uninstall(削除)。どのMCPサーバーをどのスコープで接続するかを明示的に制御したい場合は、これをスキップしてください。上記の手動のclaude mcp addコマンドを使えば、その制御が可能です。15

最適な用途: 深い実装セッション — 新機能の構築、複数ファイルにまたがるリファクタリング、複雑な問題のデバッグ、ビルド・テスト・修正ループの自律的な実行。Claude Codeの100万トークンのコンテキストウィンドウ(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エコシステムが小規模(テスト済みのコミュニティサーバーが少ない) - Hooksシステムが利用可能(v0.119.0+)だがClaude Codeほど成熟していない — イベントタイプが少なく、条件付きifフィールドがない

iOS開発でClaude CodeよりCodexを使う場面:

モデルの多様性が欲しいときにCodexを使います。一方のエージェントが書いたコードをもう一方のエージェントがレビューすることで、異なる種類のエラーを発見できます。collabワークフロー(Claudeがビルドし、Codexがレビュー)はiOS開発において効果的です。あるモデルファミリーでは正しく見えるSwiftUIのパターンに、別のモデルファミリーが微妙な問題を見つけ出すことがあるからです。Metalシェーダーや並行処理パターンは特にデュアルモデルレビューの恩恵を受けます。

3. Xcode 26.3ネイティブエージェント

概要: AppleはAIコーディングエージェントをXcodeのIntelligenceパネルに直接統合しました。Xcode 26.3以降、Xcode Settings > IntelligenceでClaude AgentとCodexをインテリジェンスプロバイダとして設定できます。

セットアップ:

  1. Xcode 26.3+を開く
  2. Settings > Intelligenceに移動
  3. 新しいプロバイダを追加:
  4. Claudeの場合:「Claude Agent」を選択し、AnthropicのAPIキーを入力
  5. Codexの場合:「Codex」を選択し、OpenAIのAPIキーを入力
  6. エージェントが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並列) なし なし
コンテキストウィンドウ 100万トークン(Opus 4.6) モデルによる プロバイダによる
複数ファイル操作 コードベース全体にアクセス可能 コードベース全体にアクセス可能 現在のファイル / 選択範囲
.pbxproj保護 フック経由 手動 N/A(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は、xcodebuildxcrun 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プロジェクトでのみMCPを使い、Webプロジェクトでは使いたくない場合に便利です)。

-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 プロジェクトの作成と依存関係の追加

日々の作業で最も重要なツール:

  1. build_sim — このツールは何百回も呼ぶことになります。ファイル、行、深刻度ごとにカテゴリ分けされたエラーとともにJSONを返します。エージェントはエラーを読み取り、該当ファイルに移動し、何もしなくても修正してくれます。

  2. test_sim — テストメソッドごとの結果を返します。エージェントは「テストが失敗しました」だけでなく、どのテストがなぜ失敗したのかを正確に把握できます。

  3. list_sims + boot_simxcrun simctl のフラグを覚えなくてもシミュレーターを管理できます。エージェントは利用可能なランタイムを発見し、適切なデバイスを選択します。

  4. discover_projs + list_schemes — プロジェクトの内省機能です。エージェントはスキーム名やワークスペース構造を推測する必要がありません。

  5. 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 にはないもの):

  1. DocumentationSearch — WWDCセッションを含むAppleの開発者ドキュメントを検索します。Apple API に関する質問では、Web検索よりも高速かつ信頼性が高いです。「HKQuantityType(.dietaryWater) は有効か?」と尋ねれば、ソースから決定的な答えが返ってきます。

  2. ExecuteSnippet — プロジェクトのコンテキスト内で Swift REPL を実行します。エージェントはアプリ全体をビルドすることなく、API の挙動を確認したり、型変換をテストしたり、式を検証したりできます。

  3. RenderPreview — SwiftUIプレビューをヘッドレスでレンダリングします。エージェントはビューがエラーなくレンダリングされるかを確認できますが、視覚的な正しさは評価できません(レンダリング結果はデータとして返され、視覚的に検査されるわけではありません)。

  4. 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」と表示される、または表示されない場合:

  1. XcodeBuildMCP が接続できない場合: Node.js がインストールされているか確認してください(node --version)。npx コマンドには Node.js 18 以上が必要です。
  2. Apple Xcode MCP が接続できない場合: Xcode 26.3 以上がインストールされており、ターミナルで xcrun mcpbridge コマンドが動作することを確認してください。ライセンス契約に同意するため、Xcodeを少なくとも一度は開いてください。
  3. 両方とも表示されない場合: Claude Code を再起動してください(新しいターミナルで claude を実行)。セッション中に登録されたMCPサーバーは、再起動するまで表示されないことがあります。

MCP を使うようエージェントに教える

MCPサーバーをインストールするのは必要ですが、それだけでは不十分です。明示的なガイダンスがないと、エージェントはBash経由で xcodebuild を実行する(構造化されていない出力、コンテキストトークンの浪費)状態に戻ってしまったり、Appleのドキュメントを調べるのにWeb検索を使ってしまう(より遅く、信頼性が低い)ことがあります。

CLAUDE.md またはエージェント定義に以下を追加してください:

## Build & Test — 必ず MCP を使用する

すべてのビルド操作において、生のシェルコマンドではなく MCP ツールを優先してください。

- **Build**: `build_sim` / `build_device`(Bash 経由の `xcodebuild` ではない)
- **Test**: `test_sim` / `test_device`(Bash 経由の `xcodebuild test` ではない)
- **Simulators**: `list_sims``boot_sim``open_sim`(Bash 経由の `xcrun simctl` ではない)
- **Debug**: `debug_attach_sim``debug_stack``debug_variables`
- **Apple ドキュメント**: `DocumentationSearch`(Apple API の WebSearch ではない)
- **Swift 検証**: `ExecuteSnippet`(Bash 経由の `swift` ではない)
- **Previews**: ヘッドレスでの SwiftUI 検証には `RenderPreview` を使用

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 を使います。Bundle ID はエンタイトルメントや HealthKit の設定にも関わります。Swift のバージョンは並行性モデルを決定づけます(async/await か完了ハンドラーか、厳格な並行性か緩やかな並行性か)。

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:
```bash
xcodebuild -scheme Return -destination 'platform=iOS Simulator,name=iPhone 16 Pro' test
```

Run tvOS tests:
```bash
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. 主要なパターンとルール

## 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 つのセクションがどのように連携するかを示す注釈付きの例を紹介します。これは、私が Banana List で使っている CLAUDE.md のパターンです。Banana List は、iCloud 同期とアプリのデータを Claude Desktop に公開するカスタム MCP サーバーを備えた、53 ファイル構成のグローサリーリストアプリです。

# 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 の述語の制約のため)

## ビルドとテスト

```bash
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` を使用してください
- View 内では `@Query`、View 以外のコードでは `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つの必須セクションをすべてカバーしている点に注目してください。

# 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

- `Workout` has many `Exercise` (cascade delete)
- `Exercise` has optional `ExerciseTemplate`
- `ExerciseTemplate` standalone (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

14ファイルのプロジェクトに対し、CLAUDE.md は40行です。執筆にかかるのはわずか10分ほどですが、エージェントの混乱による何時間もの無駄を防いでくれます。

実例 CLAUDE.md: Starfield Destroyer(SpriteKit + Metal — 32ファイル)

ゲームプロジェクトでは、フレームワーク固有のコンテキストがより多く必要になります。エージェントはシーングラフ、物理カテゴリ、ゲームステートマシンを理解する必要があります。

# 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
```

## Game State Machine

```
.menu → .playing → .paused → .playing
                 → .gameOver → .menu
                 → .bossIntro → .playing
                 → .levelComplete → .playing (next level)
```

## Metal Post-Processing

- Bloom shader: `BloomShader.metal` — multi-pass Gaussian blur + additive blend
- Uniforms: `PostProcessUniforms { float intensity; float threshold; float2 resolution; }`
- Applied after SpriteKit renders each frame via `SKView.presentScene(:transition:)`
- DO NOT modify Metal shaders without testing on device

## Build & Test

```bash
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-orderingを変更しない
- ShaderTypes.hを変更する場合は、必ずSwiftとMetalの両方の参照を更新する
- 新しい敵を追加する場合は、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
1. Compute pass: AudioCompute.metal processes FFT data → texture
2. Render pass: Shaders.metal reads texture + uniforms → visualization
3. 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ファイル):レンダーパイプラインステージ、ユニフォーム構造体、バッファ管理

過剰なドキュメント化のコストはほぼゼロです(エージェントは必要のない部分をスキップします)。ドキュメント化が不十分なコストは高くつきます(エージェントがコードベースと矛盾するパターンを生み出します)。

CLAUDE.mdチェックリスト

iOSプロジェクトのCLAUDE.mdを作成または監査する際は、このチェックリストを使用してください。

  • [ ] Bundle IDとデプロイメントターゲットを明記している
  • [ ] Swiftバージョンとアーキテクチャパターンを明記している
  • [ ] ファイル構造に各ファイルの目的をインライン注釈している
  • [ ] 正しいschemeとdestinationを指定したビルドコマンドがある
  • [ ] 正しいschemeとdestinationを指定したテストコマンドがある
  • [ ] 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ターンで完結します。

エージェントが自律的に修正できるエラー: - インポート漏れ - 型の不一致 - プロトコル準拠の不足 - 非推奨の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ファイルの編集 — 絶対NG

これは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を参照)

ワークフロー: エージェントは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ビューのbodyの複雑さ(エージェントは深くネストされたビューを作成し、再描画のオーバーヘッドを引き起こします) - Core Data / SwiftDataのフェッチ最適化(エージェントは正しいクエリを書きますが、大規模データセットでは遅くなる可能性があります)

緩和策: 実装にはエージェントを使い、Instrumentsで手動プロファイリングし、特定した最適化をエージェントに依頼しましょう。

コード署名とプロビジョニング

エージェントはエラーメッセージを読む以上の方法でコード署名の問題をデバッグできません。プロビジョニングプロファイルの管理、証明書の作成、エンタイトルメントの設定、App Storeへの申請は、根本的に人間が操作するワークフローであり、Apple Developerポータル、Keychain Access、XcodeのSigning UIが関わります。

エージェントに見えること:「Signing for ‘Return’ requires a development team.」

エージェントに見えないこと: 証明書が期限切れかどうか、プロビジョニングプロファイルにデバイスが含まれているか、Bundle IDがApp IDと一致しているか、エンタイトルメントファイルが正しいか。

緩和策: 署名はすべてXcodeのSigning & Capabilitiesタブで処理しましょう。署名失敗のデバッグをエージェントに依頼してはいけません。

複雑なMetalシェーダーのデバッグ

エージェントは構文的に正しいMetal Shading Language(MSL)を書けますが、ビジュアル出力の検証やGPU側の問題のデバッグはできません。MetalシェーダーはGPU上で実行されるため、エージェントにはシェーダーが正しいビジュアル結果を生成しているかを判断するフィードバック手段がありません。

エージェントがMetalでできること: - 説明から頂点シェーダーとフラグメントシェーダーを書く - SwiftでMetalレンダーパイプラインをセットアップする - データ並列演算用のコンピュートシェーダーを作成する - .metalファイルのコンパイルエラーを修正する

エージェントがMetalでできないこと: - シェーダー出力のビジュアル的な正しさを検証する - GPUパフォーマンス(フレームタイム、占有率、メモリ帯域幅)をデバッグする - ビジュアルアーティファクト(バンディング、精度の問題、誤った色空間)を診断する - 異なるGPUアーキテクチャでテストする(AシリーズとMシリーズの挙動の違い)

緩和策: Metalシェーダーは実機でテストしましょう。SimulatorのMetal実装は実機のGPU挙動を代表するものではありません。ビジュアルデバッグにはXcodeのGPU Frame Captureを使いましょう。

ビジュアルレイアウトの検証

エージェントはアプリのUIを見ることができません。SwiftUIのレイアウトコードを書き、コンパイルが通ることは確認できますが、結果として表示される画面が正しく見えるかどうかは判断できません。10ピクセル中心からずれているビュー、誤ったフォントウェイトを使っているビュー、要素が重なっているビューはビルドエラーを出さず、ロジックテストもすべてパスしてしまいます。

緩和策: UIの変更はビジュアルにレビューしましょう。Xcode上でSwiftUI Previews(またはApple MCPによるヘッドレスレンダリング用のRenderPreview)を使ってレイアウトを検証します。自動的なビジュアルリグレッション検出には、swift-snapshot-testingのようなライブラリでスナップショットテストを導入することも検討しましょう。


iOS開発のためのHooks

Hooksとは、エージェントのワークフローの特定のポイントで決定論的に実行されるシェルコマンドです。これは強制メカニズムであり、「.pbxprojを編集しないでください」(エージェントが無視する可能性のある提案)と「.pbxprojを編集できません」(ハードブロック)の違いを生み出します。

Hooksシステムの背景については、Claude Code hooksガイドをご覧ください。このセクションではiOS固有のHookパターンを扱います。

PreToolUse:.pbxprojへの書き込みをブロック

iOSプロジェクトで最も重要なHookです。エージェントが.pbxprojファイル、.xcodeproj/ディレクトリ、その他のXcode管理ファイルへ書き込むのをブロックします。

{
  "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ツールを使用しようとすると、Hookが実行されてファイルパスを検出し、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順序を正規化します。保存時フォーマットHookにより、エージェントが触れるすべての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:Entitlements変更のブロック

エージェントによる偶発的な変更からEntitlementsファイルを保護します。

{
  "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 Hook設定

以下が、すべての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パターンに戻ってしまうことがあります。明示的に禁止することで、これを防げます。

// 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のstrict concurrencyを対象としましょう。

// 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 handlerは使わない) - アクター間で受け渡すバリュー型は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ではナビゲーションバーが自動的にglassマテリアルを採用する。カスタムマテリアルでglass効果を手動で再現してはならない — システムのモディファイアを使うこと。」


フレームワーク別のコンテキスト

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にNSHealthShareUsageDescriptionNSHealthUpdateUsageDescriptionの両方を含める

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のsequenceやgroupの作成 - 物理ボディと接触検出のセットアップ - ゲーム状態マシンの実装 - 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アプリを保守している場合、エージェントは1つのアプリのパターンを別のアプリに適用できます:

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つのエージェントを使用します:

  1. Claude Codeが実装を書く
  2. 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の非推奨化への認識 強い(より新しい学習データ) 中程度
並行処理の競合状態 強い 強い(検出されるパターンが異なる)

デュアルレビューは、より多くのバグを見つけるためのものではなく、異なるバグを見つけるためのものです。各モデルファミリーは、パターン認識において異なる失敗モードを持っています。

複数アプリにまたがるバッチ操作

フレームワークやパターンの変更が複数のアプリに影響する場合:

# 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フックが配置されていることを確認してください。

AppleのオンデバイスLLMを使用するアプリ

アプリがAppleのFoundation Modelsフレームワークを呼び出す場合(オフラインの要約、分類、構造化出力の生成など)、エージェントはプロンプトの予算を知る必要があります。iOS 26.4では、以前の4096トークンという推測値を置き換える2つのAPIが SystemLanguageModel に追加されました:contextSize(モデルが1つの会話で受け入れる最大トークン数)と tokenCount(for:)async throws、指定されたプロンプトが実際に消費するトークン数を返す)です。16 どちらも @backDeployed(before: iOS 26.4) のため、#available のはしごを使わなくても、FMをサポートするすべてのOSバージョンで利用可能です。

プロンプト構築コードを生成する際にエージェントが従うべきパターン:

import FoundationModels

func budgetFor(prompt: String, reservedReply: Int = 256) async throws -> Int {
    let model = SystemLanguageModel.default
    let promptCost = try await model.tokenCount(for: prompt)
    let budget = model.contextSize - promptCost - reservedReply
    guard budget > 0 else { throw ContextError.promptTooLong }
    return budget
}

アプリが SystemLanguageModel を扱う場合は、このパターンをCLAUDE.mdに追加してください。これがないと、エージェントは古い4096のハードコードに戻り、より大きなコンテキストウィンドウを搭載したデバイスでプロンプトを暗黙的に切り詰めてしまいます。tokenCount(for:)async throws シグネチャは不可欠です — 同期版を貼り付けたエージェントはコンパイルに失敗します。


実例ケーススタディ

抽象的なアドバイスは簡単です。ここでは、agent支援によるiOS開発が実践でどう機能するか(失敗例も含めて)を示す、8つのアプリからの具体的なシナリオを紹介します。

ケーススタディ1:Returnへのテレビアプリの追加(成功)

タスク: すでにiOS版とwatchOS版があった瞑想タイマーReturnに、tvOSターゲットを追加する。テレビアプリには、Siri Remoteによるナビゲーション、大画面UI、iOSアプリとの設定同期が必要でした。

agentがうまくやったこと: - 既存のiOS TimerManagerを読み込み、Live ActivitiesとHealthKit(tvOSでは利用不可)を除いたTVTimerManagerを作成 - Siri Remoteのフォーカスナビゲーション用のカスタムボタンスタイル(TVCapsuleButtonStyleTVCircleButtonStyle)を作成 - ホイールピッカー(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の変更) - テレビターゲットへのApp Groups entitlementの設定 - 既存のスキームへのテレビターゲット追加、または新規スキーム作成 - agentが作成したすべてのSwiftファイルをテレビターゲットに手動で追加 - Siri Remoteのナビゲーションを手で動作確認(agentはフォーカス挙動を評価できません)

結果: 15個の新しいSwiftファイル、完全に動作するテレビアプリが、agent支援作業約3時間で完成しました。私の見積もりでは、agentが実装作業の約80%を処理し、Xcode UIでのインタラクションが必要な部分(entitlements、ターゲット設定、capabilityフラグ)と実機Apple TVでの手動フォーカステストは私が担当しました。このコードベースで一人で同等の作業をした場合、agentなしで出荷した類似機能を基準にすると、数日がかりの作業となっていたでしょう。

ケーススタディ2:amp97でのMetalシェーダーデバッグ(部分的失敗)

タスク: オシロスコープシェーダーにエネルギーベースの強度システムを追加する。可視化はオーディオエネルギーに合わせて脈打つはずでした。

起こったこと: 1. agentはuEnergy uniformとHDRトーンマッピングを追加する有効なMetalシェーダー修正を書きました 2. コードはエラーなしでコンパイルされました 3. デバイス上で、可視化は完全に真っ白に──強度係数が10倍高すぎたのです(0.30ではなく3.5) 4. agentは白い画面が見えないため、フィードバック信号がありませんでした 5. 私が視覚的に問題を特定し、係数を下げるようagentに依頼しました 6. agentは係数を下げましたが、エネルギーステートマシン全体が複雑すぎて、別の形でvisualizerを壊してしまいました 7. 完全にリバート──2つのコミット(67959edcda4830)が869d914でリバートされました

教訓: Metalシェーダーは、フィードバックループが断絶しているため、agent支援開発において最も難しい領域です。agentは構文(コンパイル可能)とセマンティクス(正しい型)を検証できますが、出力(見た目が正しいか)は検証できません。ビジュアル挙動を変えるシェーダー修正には、すべて実機での人間による検証が必要となります。

この後CLAUDE.mdに追加したもの: 「オシロスコープシェーダーのエネルギーステート修正は、係数テストを極めて慎重に行わない限り試みないこと。前回の試みでは10倍高すぎる係数でvisualizerを壊した」

ケーススタディ3:Banana ListにおけるSwiftDataマイグレーション(成功)

タスク: データモデルをV1からV2へ移行し、GroceryItemquantityフィールドを追加し、リレーションを持つ新しいCategoryモデルを追加する。

agentがしたこと: 1. 既存のV1モデル定義を読み込み 2. 新しいフィールドとリレーションを持つV2モデル定義を作成 3. SchemaMigrationPlanプロトコルに準拠したGroceryMigrationPlanを記述 4. V1toV2マイグレーションステージを実装:デフォルトのquantity: 1category: nilを追加 5. 新しいフィールドをサポートするようすべてのビューを更新 6. プレビュー用にSampleData.swiftを更新 7. MCPでビルドしてテストを実行──すべてパス 8. マイグレーション固有のユニットテストを作成

カギ: SwiftDataマイグレーションがApple公式ドキュメントとトレーニングデータに十分に表現された明確に定義されたプロトコルパターンに従っているため、agentは成功しました。CLAUDE.mdがV1モデルを明示的にドキュメント化していたため、agentは何から移行しているかを理解していたのです。

ケーススタディ4:ReturnでのiCloudセッション同期(複雑だが成功)

タスク: デバイス間の瞑想セッションログを実装する。Apple TVやMacで完了したセッションは、HealthKitログのためにiPhoneと同期する必要がありました。

agentが生み出したもの:

┌─────────────┐     ┌─────────────┐     ┌─────────────┐
    tvOS              Mac              Watch     
 TVTimerMgr        TimerMgr          WatchTimer  
└──────┬──────┘     └──────┬──────┘     └──────┬──────┘
                                             
       └───────────────────┼───────────────────┘
                           
                           
              ┌────────────────────────┐
                   SessionStore       
                (iCloud Key-Value)    
              └───────────┬────────────┘
                          
                          
              ┌────────────────────────┐
                iPhone (on foreground)
                 Write to HealthKit  
              └────────────────────────┘

agentは: 1. UUID、日付、duration、ソースデバイス、HealthKit同期ステータスを持つMeditationSessionデータモデルを作成 2. iCloud同期のためにNSUbiquitousKeyValueStoreを管理するシングルトンSessionStoreを構築 3. マージ競合解決(UUIDベースの重複排除)を実装 4. プラットフォーム固有の適応を伴うSessionHistoryViewを追加(iOSではスワイプで削除、tvOSではフォーカスベース) 5. 他のデバイスからのセッション用にiPhone側のHealthKit同期を組み込み

反復が必要だったこと: 初期実装では、iPhoneアプリがバックグラウンドで起動するケース(同期用のフォアグラウンド通知がない)を処理していませんでした。agentには具体的な指示が必要でした:「バックグラウンドのKV変更で同期をトリガーするには、NSUbiquitousKeyValueStore.didChangeExternallyNotificationを使用してください」。このヒントの後、実装は正しくなりました。

教訓: アーキテクチャが明確に記述されている場合、agentはマルチプラットフォームのアーキテクチャパターンをうまく扱います。iCloud同期パターンは些細ではありませんが、agentが理解できるドキュメント化されたApple公式パターンに従っています。エッジケース(バックグラウンド同期)は十分にドキュメント化されていないため、人間のドメイン知識が必要でした。

ケーススタディ5:Starfield DestroyerでのGame Center統合(成功)

タスク: スペースシューターにGame Centerのリーダーボードと実績を追加する。

agentがうまくやったこと: - アプリのエントリポイントでGKLocalPlayer.local.authenticateHandlerを実装 - スコア送信と実績報告のメソッドを持つGameCenterManagerを作成 - すべてのGame Center操作の前に認証状態チェックを追加 - オフラインケースを優雅に処理(Game Centerなしでもゲームはプレイ可能、再接続時に送信) - 8隻の艦船進行システムに対応する実績定義を作成

手動作業が必要だったこと: - App Store Connectでのリーダーボードと実績の作成(Webポータル、agentからアクセス不可) - XcodeでのGame Center entitlementの設定 - サンドボックスのGame Centerアカウントでのテスト(実機での手動サインインが必要)


エージェントとともに進めるプロジェクトのライフサイクル

新規 iOS プロジェクトの開始

エージェントの支援を受けながら新規プロジェクトを開始する際の最適なワークフローは、次のとおりです。

フェーズ 1: 人間によるセットアップ(15〜30 分) 1. Xcode プロジェクトを作成する(File > New > Project) 2. 署名と Capability を設定する 3. デプロイメントターゲットとサポート対象のデスティネーションを設定する 4. 必要な Entitlement を追加する(HealthKit、Game Center など) 5. プロジェクトのアイデンティティとルールを記載した初期 CLAUDE.md を作成する

フェーズ 2: エージェントによる実装(数時間〜数日) 1. エージェントがデータモデルを作成する(SwiftData、Core Data、またはプレーンな struct) 2. エージェントが文書化されたパターンに従ってビューを構築する 3. エージェントが manager / service クラスにビジネスロジックを実装する 4. エージェントがユニットテストを書く 5. MCP を介したビルド・テスト・修正のループ(自律実行)

フェーズ 3: 人間による統合(30〜60 分) 1. エージェントが作成したファイルを Xcode のターゲットに追加する 2. 署名と Entitlement を確認する 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 ビュー Yes エージェントは宣言的 UI を得意とする
SwiftData モデルの変更 Yes 定義が明確でテスト可能
ユニットテスト Yes 機械的でパターン化しやすい
リファクタリング Yes 体系的かつ複数ファイルにまたがる作業
ビルドエラーの診断 Yes(MCP 経由) 構造化されたフィードバックループが得られる
新規 Xcode ターゲット No Xcode UI と .pbxproj の変更が必要
署名とプロビジョニング No ポータルベースで、エージェントからアクセス不可
ビジュアルの仕上げ No 人間の美的判断が必要
Metal シェーダーのチューニング No GPU 上での実機テストが必要
App Store への提出 No ポータルおよび Xcode Organizer の操作が必要
パフォーマンスプロファイリング No 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内のファイルアノテーションが重要な理由はここにあります。すべてを読み込まなくても、適切なファイルへとエージェントを導いてくれるのです。

大規模プロジェクトのための戦略

  1. 詳細なCLAUDE.mdファイルアノテーション — エージェントはファイルマップを読み、関連するファイルへ直接ナビゲートします
  2. サブエージェントへの委任 — 探索や調査をサブエージェントに任せます(クリーンなコンテキストで、サマリーを返してくれます)
  3. 焦点を絞ったプロンプト — 「設定を更新して」より「SettingsView.swiftを修正して新しいトグルを追加して」の方が効果的です
  4. セッションの境界 — 関連性のない機能には、長いセッションを延長するのではなく、新しいセッションを開始しましょう
  5. /compactの活用 — Claude Codeのcompactionコマンドが会話を要約し、コンテキストを解放します

MCPのトークン効率

MCPを使う最も強力な根拠の1つ。構造化された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」

エージェントがスキーム名を推測しています。修正方法:

このプロジェクトをビルドする前に、discover_projsとlist_schemesを使って
正しいスキーム名を見つけてください。

または、CLAUDE.mdにスキーム名を明示的に追加します:

## Build
Primary scheme: `Return` (iOS)
Watch scheme: `ReturnWatch` (watchOS)
TV scheme: `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 tools not appearing in 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つの解決策があります:

  1. CLAUDE.mdに明示的なガイダンスを追加するエージェントにMCPの使用を教えるを参照)
  2. 直接プロンプトする: 「Bash経由のxcodebuildではなく、build_sim MCPツールを使ってください」

「ビルドは成功したのにエージェントが失敗を報告する」

XcodeBuildMCPはxcodebuildの出力をパースします。ビルドがエラーのように見える警告(非推奨警告でよくあります)を生成すると、エージェントが結果を誤解釈する可能性があります。MCPレスポンスの実際のステータスフィールドを確認してください。

「シミュレーターが起動中にハングする」

すべてのシミュレーターを終了して再起動します:

xcrun simctl shutdown all
xcrun simctl boot "iPhone 16 Pro"

または、エージェントに依頼します:

すべてのシミュレーターをシャットダウンしてから、新しい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

どのエージェントランタイムから始めるべきですか?

XcodeBuildMCPを伴うClaude Code CLIです。最も深いMCP統合、最も成熟したフックシステム、そしてiOSプロジェクト全体をワーキングメモリに保持できる1Mコンテキストウィンドウ(Opus 4.6)を備えています。まずここから始め、ワークフローが成熟してきたらレビュー用に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アプリの場合:

  1. 環境固有の設定には.xcconfigファイルを使う
  2. .xcconfigファイルを.gitignoreに追加する
  3. Info.plistのビルド設定経由で設定値を参照する
  4. CLAUDE.mdに必要なシークレットを文書化する(実際の値は含めない)
## Configuration

API base URL and keys are in `Config.xcconfig` (not committed).
Required keys:
- `API_BASE_URL` — Backend server URL
- `API_KEY` — Authentication token

Create `Config.xcconfig` from `Config.xcconfig.template`.

エージェントはキーが存在することと、それらが使われている場所は知りますが、実際の値を見ることは決してありません。

SwiftUIアニメーションについて——エージェントは書けますか?

エージェントはアニメーションコードを構文的にはうまく書きますが、結果を視覚的に検証することはできません。シンプルなアニメーション(.animation(.spring()).transition(.slide)withAnimation { })は正しい結果を生み出します。複雑で多段階の、精密なタイミングを伴うアニメーションには、エージェントにはできない視覚的な反復作業が必要となります。

効果的: 「タイマーが状態間を遷移するときにスプリングアニメーションを追加してください。」

非効果的: 「タイマーアニメーションを心地よく感じるようにしてください。」(主観的で、視覚的なチューニングが必要です。)

エージェントはエラーハンドリングのパターンをどのように扱いますか?

非常にうまく扱います。エージェントはSwiftのdo/catchResultasync 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 モデルバージョン
- 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の100万トークンウィンドウであれば、Claude Codeは約50〜70個のSwiftファイルをアクティブな作業メモリとして同時に保持できます。これより大きなプロジェクトでは、エージェントはファイル検索と選択的な読み込みを使い、コードベースのサブセットを扱います。100以上のファイルがあるプロジェクトでも問題なく動作します。エージェントはすべてをコンテキストに保持するのではなく、必要に応じてファイルを読み込むだけです。

実用上の限界はファイル数ではなく、コードベースの一貫性です。詳細なCLAUDE.mdが整備された200ファイルのプロジェクトのほうが、ドキュメントがない30ファイルのプロジェクトよりも良い結果を生みます。

iOS開発でエージェントを使うのにSwiftの知識は必要か?

エージェントの出力をレビューし、アーキテクチャ上の意思決定ができる必要があります。すべての行を自分で書く必要はありませんが、エージェントが誤った選択をしたときに気づける程度にはSwiftを理解しておく必要があります。とりわけ並行性、メモリ管理、フレームワーク固有のパターンに関してはそうです。エージェントは既存スキルの10倍の増幅器であり、その代わりではありません。

エージェントはSwiftファイルのマージコンフリクトをどう扱うのか?

エージェントはSwiftソースファイルのマージコンフリクトを確実に解決します。標準的なコンフリクトマーカー(<<<<<<<=======>>>>>>>)はあらゆるエージェントランタイムで十分に理解されています。ただし、.pbxproj ファイルのマージコンフリクトは依然として手動解決のタスクです。エージェントに.pbxprojのコンフリクトを解決するよう依頼してはいけません。

iOS開発でエージェントを動かすコストはどれくらいか?

AnthropicのMaxプラン(Opus 4.6、100万コンテキスト)では、典型的なiOS開発セッションは30〜120分続き、20万〜80万トークンを処理します。MCPツール呼び出しのオーバーヘッドはわずかです(構造化されたJSONレスポンスは生のビルド出力に比べてトークン効率が良いのです)。コストは他のコードベースでClaude Codeを動かす場合と同等であり、iOS開発がWeb開発より特別に高くつくことも安くつくこともありません。

UIKitプロジェクトでエージェントを使えるか?

使えますが、エージェントはSwiftUIの方がより効果を発揮します。UIKitはより多くのボイラープレートを必要とし、宣言的構造に乏しく、エージェントが編集できないInterface Builderファイルを伴うことがしばしばあります。UIKitプロジェクトをお持ちなら、モデル層とビジネスロジックにエージェントを使い、UIは手作業で扱う、もしくはビューを段階的にSwiftUIへ移行することを検討してください。

エージェントはローカライゼーションをどう扱うのか?

エージェントは .xcstrings(Xcode String Catalog)ファイルの作成と編集を効果的に行います。新しいローカライゼーションキーを追加し、翻訳を提供し、複数言語間で一貫性を保つことができます。.xcstrings ファイルの構造化されたJSONフォーマットはエージェントにとって扱いやすいものです。.strings ファイル(レガシーフォーマット)でもエージェントは良好に機能します。キーと値のフォーマットは単純明快だからです。


iOSにおけるエージェントの典型的なミス(およびその防ぎ方)

これらは、8つのiOSプロジェクトにわたる数千回のエージェントとのやり取りの中で観察してきた、繰り返し起こるエラーです。それぞれに防止策があります。

ミス1: Observableパターンの混在

何が起きるか: エージェントがあるファイルでは @Observable を使い、別のファイルでは ObservableObject を使ったり、すでにObservableである @Model クラスに @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.publishNotificationCenter、completion handler において発生します。

防止策: 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 クラスを作成してしまい、UI更新がメインスレッド外で発生した際にSwift 6.2の並行性警告や実行時クラッシュを引き起こします。

防止策:

## Concurrency Rule
ALL @Observable classes MUST be @MainActor:
```swift
@Observable
@MainActor
final class SomeManager { }
```

ミス4: NavigationLinkでのDestinationクロージャの使用

何が起きるか: エージェントが、型安全な NavigationLink(value:) + .navigationDestination(for:) パターンではなく、非推奨の NavigationLink(destination:label:) を使用してしまいます。

防止策:

## 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には次のように記述します。

## 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: プラットフォームの利用可否の未対応

何が起きるか: エージェントが、tvOS(HealthKitが利用不可)向けにコンパイルされる共有コードでHealthKitを使用したり、watchOSコードでActivityKitを使用したりします。

防止策:

## 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行程度のユーティリティ関数で済むはずの内容に対して、エージェントがプロトコル、プロトコル拡張、具象実装、ファクトリ、依存性注入コンテナまで作成してしまいます。

防止策: シンプルさの原則を含めます。

## 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

率直な評価

AIエージェントとともに8つのiOSアプリをリリースしてきた経験から、要点をまとめます。

エージェントが変えたもの: 実装スピードです。数日かかっていた作業が数時間で終わるようになりました。SwiftUIのビュー、SwiftDataのモデル、ユニットテスト、リファクタリング——これらは現在、主にエージェントが生成し、人間がレビューするかたちになっています。

エージェントが変えなかったもの: アーキテクチャの判断、ビジュアルデザイン、パフォーマンス最適化、App Store提出。これらは依然として人間が主導する領域です。

倍率は確かに存在しますが、限界もあります。 8アプリのポートフォリオ全体を主観的に見積もると、適切なMCPとフックの設定が整ったドキュメント完備のプロジェクトでは、機能実装までの時間が3〜5倍改善されています。これは対照群と比較した数値ではなく、同じコードベース内でエージェント支援による機能実装と単独作業による同等の作業を経過時間ベースで比較したものです。ドキュメントもフックもないプロジェクトでは、せいぜい1.5〜2倍程度の改善にとどまります——エージェントが構築よりも推測に時間を費やしすぎてしまうのです。18

投資する価値があるもの: 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年4月28日 エージェントワークフロー向けの推奨Xcodeを26.4+に引き上げ(26.4.1、2026年4月16日、ビルド17E202が最新の安定版でバグ修正のみ)。Xcode 26.4の機能(2026年3月24日、ビルド17E192)のうち、エージェントが書くテストやローカライズに有用なものを引用:Swift Testingの画像添付、Issue.recordでの重大度指定、添付クラッシュログ付きのUIテストクラッシュ警告(具体的にはXCUIApplication(bundleIdentifier:) / XCUIApplication(url:)を使うアプリ向け)、String Catalogエディタの改善。手動のMCPセットアップに代わる選択肢として、xcodebuildmcp init自動インストーラー(v2.1.0+、2026年2月23日)を追加。
2026年4月27日 App Store Connect:2026年4月28日からXcode 26+での提出が必須に。Foundation ModelsにSystemLanguageModel.contextSizeおよびtokenCount(for:)APIsが追加(iOS 26.4にバックデプロイ)——エージェント生成によるFMプロンプト予算管理コードのパターンを追記。iOS 26.4.2(4月22日)およびiOS 26.5 beta 3(4月20日)はリリースされましたが、エージェントツールチェーンに影響する変更はありません。
2026年4月13日 初版公開。8アプリ、3つのランタイム、MCPセットアップ、CLAUDE.mdパターン、フック、ケーススタディを掲載。

参考文献


  1. XcodeBuildMCP にはデフォルトで Sentry テレメトリが含まれています。プロジェクトのプライバシードキュメントに、送信される内容(エラーメッセージ、スタックトレース、場合によってはファイルパス)が詳細に記載されています。XCODEBUILDMCP_SENTRY_DISABLED=true 環境変数で完全にオプトアウトできます。 

  2. Anthropic、「Model Context Protocol Specification」、modelcontextprotocol.io/specification。MCP 仕様は、XcodeBuildMCP と Apple の Xcode MCP の両方が実装する JSON-RPC トランスポート、ツール検出、リソースプロトコルを定義しています。 

  3. XcodeBuildMCP、github.com/getsentry/XcodeBuildMCP。Sentry がメンテナンスするオープンソースです。シミュレータ、デバイス、デバッグ、UI 自動化ワークフローにわたる 59 のツールを提供します。セマンティックバージョニングとチェンジログを採用しています。 

  4. Apple は Xcode 26.3 のインテリジェントな開発者ツールイニシアチブの一環として Xcode MCP サーバーを導入し、MCP を AI コーディングアシスタントと Xcode ツールチェーン間のインターフェース層と位置付けました。公式ドキュメントは Xcode Release Notes をご覧ください。 

  5. Rudrank Riyam、「Exploring Xcode Using MCP Tools」、rudrank.com/exploring-xcode-using-mcp-tools-cursor-external-clients、2026年。Apple の MCP ツール数、XPC への依存、ドキュメント検索機能を独立した立場から確認しています。 

  6. 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 インターフェースの妥当性を裏付けています。 

  7. Claude Code CLI ドキュメント、code.claude.com。フックシステム、MCP 設定、サブエージェント委任、エージェント定義について解説しています。 

  8. SwiftFormat、github.com/nicklockwood/SwiftFormat。一貫したコードスタイルのために PostToolUse フックで使用される Swift フォーマットツールです。 

  9. XcodeBuildMCP 公式サイト、xcodebuildmcp.com。CLI の出力例から 59 個の MCP ツールを確認できます。ツールカテゴリ:シミュレータ、デバイス、デバッグ、UI 自動化。Homebrew または npx でインストールできます。 

  10. Swiftjective-C、「Agentic Coding in Xcode 26.3 with Claude Code and Codex」、swiftjectivec.com、2026年2月。Xcode 26.3 が Settings > Intelligence 経由でネイティブの Claude Agent と Codex ランタイムサポートを搭載していることを確認しています。xcrun mcpbridge 経由で 20 の MCP ツールが公開されます。 

  11. Blake Crosley、「Two MCP Servers Made Claude Code an iOS Build System」、blakecrosley.com/blog/xcode-mcp-claude-code、2026年2月。同じ筆者の iOS 開発ワークフローからのセットアップ手順と実環境での結果を紹介しています。 

  12. Apple Developer News、「Upcoming Requirements」。2026-04-28 のエントリ:「App Store Connect にアップロードされるアプリは、iOS 26、iPadOS 26、tvOS 26、visionOS 26、または watchOS 26 用の SDK を使用して Xcode 26 以降でビルドする必要があります」。macOS はこの要件で列挙されているプラットフォームには含まれていません。 

  13. Apple、「Xcode 26.4 Release Notes」。Xcode 26.4(2026-03-24、ビルド 17E192)。リリースノートからの引用機能:Swift Testing が CGImageNSImageUIImageCIImage 経由で画像添付をサポート、Issue.record が重要度レベルを受け付ける、一部の UI テストアプリのクラッシュ(具体的には XCUIApplication(bundleIdentifier:) または XCUIApplication(url:) を介して操作されたアプリ)はテストを失敗させる代わりにクラッシュログを添付した警告として報告される、String Catalog エディタにエントリのカット/コピー/ペースト、言語の削除、既存言語からの翻訳の事前入力、および BUILD_ONLY_KNOWN_LOCALIZATIONS 設定が追加されました。 

  14. Apple Developer News、「Xcode 26.4.1 (Build 17E202) Now Available」、2026-04-16。バグ修正のみのドットリリースで、26.4 より前の iOS / macOS / visionOS でシンボルが欠落することによる MetricKit のクラッシュと、Swift の非同期スタック割り当てのバグ(swift_asyncLet_finish での「freed pointer was not the last allocation」)を修正しています。 

  15. getsentry/XcodeBuildMCP の v2.1.0 リリース、2026-02-23。エージェントスキルと MCP 設定を 1 ステップでインストールする xcodebuildmcp init CLI コマンドを追加し、スタンドアロンの install-skill.sh スクリプトを置き換えました。Claude Code、Cursor、Codex を自動検出し、--print(サポートされていないクライアント向けに設定を stdout に書き出す)と --uninstall(削除)をサポートします。 

  16. InfoQ、「Apple Adds Context Window Management to Foundation Models」、2026年3月。新しい SystemLanguageModel.contextSize および tokenCount(for:) API について解説し、@backDeployed(before: iOS 26.4) アノテーションを確認しています。これまでコミュニティで推測されていた 4096 トークンのハードコードに代わるものです。 

  17. ファイル数は、2026-04-27 に 8 つのプライベートアプリリポジトリそれぞれに対して find . -name '*.swift' -not -path '*/Tests/*' | wc -l を実行して得られたものです。テストファイルは除外しています。合計値は §The Portfolio のアプリ別内訳テーブルと整合しています。 

  18. 対照群との比較ではなく、主観的な実時間推定です。3〜5倍という数字は、2026年のエージェント支援機能と、エージェントワークフロー導入前に同じコードベースで筆者が単独で出荷した同等の機能との「機能までの時間」比較を、筆者の記憶から振り返ったものです。ベンチマークではなく、MCP + フックのセットアップ後に期待できる目安としてご理解ください。 

NORMAL ios-agent-development.md EOF