Apple Foundation ModelsでClaudeを使い分ける — iOS/macOSアプリ実装ガイド
Apple純正のFoundation ModelsフレームワークにClaudeを組み込むSwiftパッケージの使い方。認証方式の選び方とオンデバイスモデルとの使い分けを扱います。
Apple Foundation ModelsでClaudeを使うとはどういうことか
Claude for Foundation Modelsは、ClaudeをApple純正のFoundation Modelsフレームワークにサーバーサイド言語モデルとして組み込むSwiftパッケージです。ClaudeはフレームワークのLanguageModelプロトコルに準拠します。そのため、Appleのオンデバイスモデルと同じLanguageModelSession APIで、respond(to:)、ストリーミング、ガイド付き生成、ツール呼び出しを扱えます。
リクエストはアプリからClaude APIへ直接送られ、Appleはリクエスト経路に入りません。プロンプトも応答もAppleは見ません。利用料金はAnthropicアカウントに標準のAPI料金で課金されるため、ワークスペースにクレジット残高か有効な支払い方法が必要です。どちらのモデルを使うかはセッションごとに選べます。
このパッケージが提供しているのは、Foundation Modelsが規定するLanguageModelプロトコルへのClaudeの適合です。汎用のMessages APIクライアントではありません。公開されているのは、プロバイダー適合部分と、そこに到達するための設定型です。設定型はClaudeLanguageModel・ClaudeModel・AuthMode・ClaudeServerToolの4つです。Messages APIへ直接アクセスしたい場合は、別言語のクライアントSDKを使います。
導入の前提条件とインストール
必要な環境は次のとおりです。
- iOS 27 / macOS 27 / visionOS 27 / watchOS 27(いずれもベータ)
- Xcode 27(ベータ)
- 開発用のClaude APIキー(本番の認証方式は後述)
Package.swiftに依存を追加します。
dependencies: [
.package(url: "https://github.com/anthropics/ClaudeForFoundationModels.git", from: "0.1.0")
]Xcodeから追加する場合は「File」→「Add Package Dependencies…」でリポジトリURLを入力します。ターゲットの依存関係にClaudeForFoundationModelsを加え、FoundationModelsと並べてimportします。
最小構成でClaudeを呼び出す
エントリーポイントはClaudeLanguageModelです。LanguageModelSessionに渡せば、あとはAppleの標準的なセッションAPIと同じ書き方で呼び出せます。
import FoundationModels
import ClaudeForFoundationModels
let model = ClaudeLanguageModel(
name: .sonnet5,
auth: .apiKey(ProcessInfo.processInfo.environment["ANTHROPIC_API_KEY"] ?? "")
)
let session = LanguageModelSession(model: model)
let response = try await session.respond(to: "Plan a 4-day trip to Buenos Aires.")
print(response.content)モデル名はClaudeModelの値で指定します。.opus5のようにコンパイル済みの定数を使うのが基本です。各定数はサンプリングパラメータ、effortの水準、思考の可否、構造化出力対応、画像入力対応といったモデルの能力(capabilities)を内蔵しています。パッケージがまだ定数を持たない新しいモデルIDを使う場合は、能力を自分で宣言します。
let model = ClaudeModel(
id: "claude-experimental-x",
capabilities: .init(samplingParams: false, effortLevels: [.low, .high])
)自動推測のショートカットは意図的に用意されていません。モデルが受け付けないフィールドを送るとハードエラーになるため、能力の宣言はここで正確に行う必要があります。
パッケージのソースでは、能力は宣言しなければ非対応として扱われます。
初期化子はserverToolsのほかにbaseURL(既定はhttps://api.anthropic.com)とtimeoutも受け付けます。動く例を通しで見たい場合は、リポジトリ同梱のExamples/ClaudeExampleが使えます。会話をターミナルへストリーミングするコマンドラインターゲットで、--searchフラグを付けるとその会話でサーバーサイドWeb検索が有効になります。実行にはmacOS 27のホストが必要です。
effortはfixedEffort:でリクエストごとに固定できます。フレームワーク側の推論ヒントより優先されます。
フレームワークの推論レベルはリクエストごとにeffortへ対応づけられます。リポジトリのREADMEでは、.lightがlow、.moderateがmedium、.deepがhighです。それより上のxhighやmaxは.custom("xhigh")のようにカスタム値で渡します。モデルが受け付けないレベルは落とされます。推論レベルはあくまでヒントで、契約ではないからです。effortを何も送らなければAPIはhighを既定値として扱います。
認証方式の使い分け — App Attest・プロキシ・APIキー
auth:パラメータに渡す認証情報は3種類あり、開発段階と本番運用で使い分けます。
| 方式 | 適した段階 | バックエンドの要否 | 制約 |
|---|---|---|---|
.apiKey | 適した段階開発中の検証 | バックエンドの要否不要 | 制約バイナリからキーが抽出可能。本番では使わない |
.appAttest | 適した段階本番(バックエンドを持たない場合) | バックエンドの要否不要 | 制約実機必須。ClaudeのAPIを直接呼ぶ構成のみ対応(Bedrock/Google Cloud/Microsoft Foundry経由では不可) |
.proxied | 適した段階本番(自前バックエンドを持つ場合) | バックエンドの要否必要 | 制約自前プロキシがヘッダーで呼び出し元を認可する設計が前提 |
App Attestは、AppleのApp Attestサービスでアプリの正当性を証明し、Anthropicがワークスペースに課金される短命アクセストークンを発行する仕組みです。アプリにAPIキーを同梱せず、プロキシの運用も不要になります。ただしシミュレーターやSecure Enclaveのないハードウェアでは動作しないため、開発中は.apiKey、実機での本番検証は.appAttestと使い分けます。
導入にはApple Developer Team IDと、組織のadmin・owner・primary owner権限が必要です。手順は次の4つです。
App Attestを有効にする手順
- 1
Xcodeでケイパビリティを追加
ターゲットの「Signing & Capabilities」に「App Attest」を加えます。READMEによると、エンタイトルメントは
com.apple.developer.devicecheck.appattest-environmentで、明示的に登録したApp IDが前提です。 - 2
App integrationsを開く
Claude Consoleで、対象ワークスペースの設定にある「App integrations」を開きます。
- 3
アプリを登録する
「Create app integration」を押し、名前・Apple Developer Team ID・バンドルID(最大32個)を入力します。
- 4
クライアントIDを渡す
integrationの「Overview」タブから
clid_...をコピーし、アプリ側の設定に渡します。READMEによると、このIDは公開しても安全な設定値で、アプリのバイナリに入れて構いません。
発行されるトークンはワークスペースにスコープされ、1時間で失効し、Messages APIの呼び出しのみを認可します。エンドユーザーの識別情報は含まれません — App Attestが証明するのはアプリの身元であってユーザーの身元ではないため、ユーザー単位の制御はアプリ側で実装します。侵害されたアプリや廃止したアプリは、Consoleでintegrationを選んで「Revoke」で失効させます。未失効のトークンも一緒に無効になり、登録済みの端末は新しいトークンを取れなくなります。失効は取り消せないため、復旧には新しいintegrationの作成が必要です。
ClaudeLanguageModel(
name: .sonnet5,
auth: .appAttest(clientID: "clid_...")
)初回の端末では、Appleでの証明に数秒かかります。READMEによると、パッケージはこの待ちを最初のプロンプトでなく起動時に払えるよう、authenticateIfNeeded()を用意しています。
try await model.authenticateIfNeeded()未登録のクライアントIDや非対応の端末では、この呼び出しが例外を投げます。証明に失敗した場合の型はClaudeError.attestationFailedです。ユーザーが最初のプロンプトを送る前に、アプリ側で失敗を知れます。session.prewarm()でも証明は始まりますが、こちらは結果を返さないヒントなので、失敗に反応したいならauthenticateIfNeeded()のほうを使います。2回目以降はキーチェーンのトークンを再利用し、期限切れの更新に証明のやり直しは要りません。認証情報は端末に紐づき、同期もバックアップもされません。
プロキシ構成では、baseURLで指定した自前のリレーがサーバー側でClaude API資格情報を付与するため、アプリはキーを持ちません。headersは全リクエストに付くので、プロキシはそれで呼び出し元を認可できます。何も要らなければ[:]を渡します。プロキシが受けるのは標準のMessages APIリクエストで、x-api-keyヘッダーを付けてhttps://api.anthropic.comへ転送します。
ClaudeLanguageModel(
name: .sonnet5,
auth: .proxied(headers: ["X-App-Token": "..."]),
baseURL: URL(string: "https://api.yourapp.com/claude")!
)オンデバイスモデルとClaudeの使い分け判断
オンデバイスモデルかClaudeか
Appleのオンデバイスモデル
高速でプライバシー面も強く、オフラインでも動きます。サイズは軽量なタスク向けに絞られています。
Claude
大きなコンテキスト、高度な推論、Web検索やコード実行のようなサーバーサイドツールが要る場面で使います。
公式ドキュメントは、軽い処理をオンデバイスに任せ、手に負えない場面でClaudeへエスカレーションする切り分けを示しています。同じLanguageModelSession APIを使うため、切り替えはmodel:引数を差し替えるだけで済みます。
サーバーサイドツールと構造化出力の設定
サーバーサイドツール(Web検索・Web Fetch・コード実行)はAnthropicのインフラ上で1往復のリクエスト内に完結し、デバイス側で呼び出すものがありません。serverTools:はLanguageModelSessionではなくClaudeLanguageModel側で設定します。会話ごとに異なるツール構成を使いたい場合は、ClaudeLanguageModelのインスタンスを会話ごとに分けます。
let model = ClaudeLanguageModel(
name: .sonnet5,
auth: auth,
serverTools: [
.webSearch(maxUses: 5),
.codeExecution,
]
).webSearchと.webFetchは、検索範囲をドメイン単位で絞り込めます。Swiftの引数はdomains:で、.unrestricted(既定)・.allowing([...])・.blocking([...])のいずれかを渡します。許可リストとブロックリストは併用できません。ドキュメントにあるallowedDomains・blockedDomainsは、送信時のAPI側の名前です。maxUsesは両方に共通です。サーバーサイドツールの呼び出し履歴はClaudeServerToolSegmentというカスタムセグメントとしてトランスクリプトに残ります。tools:で登録したクライアントサイドツールの呼び出しとは区別して追跡できます。
一方、フレームワーク標準のクライアントサイドツール(tools:配列)は変更なく動きます。Toolに準拠した型をLanguageModelSessionに渡すと、Claudeが呼んだときにフレームワークがデバイス上で実行します。
構造化出力は@Generableで型を注釈し、generating:で要求します。
@Generable
struct Trip {
@Guide(description: "Destination city") var destination: String
@Guide(description: "Length in days") var days: Int
}
let response = try await session.respond(to: "Plan a trip to Tokyo.", generating: Trip.self)選んだモデルが構造化出力に非対応の場合、パッケージは黙って劣化させずLanguageModelError.unsupportedGenerationGuideを投げます。Agent SDKの構造化出力と同じく、型安全な出力を前提に設計を組めます。
READMEによると、トランスクリプトの応答エントリでは、モデルが生成した順にセグメントが並びます。文章は文章のセグメント、検索や取得やコード実行の位置は中身が空のセグメントです。entry.claudeServerToolActivity(for:)で空セグメントの実体を引けば、検索の様子を回答と並べて描けます。ストリーミング中も同じ要領です。次のターンに必要な思考の署名や検索結果、引用は、予約済みのclaude.contentメタデータキーに保存され、自動で送り返されます。この値は中身を見ない前提で扱います。
フォールバックとユーザープロファイル
パッケージ0.2.0(CHANGELOGでは2026年9月9日)以降のREADMEには、ドキュメント本体に載っていない設定が2つあります。
Claudeのモデルには、サイバーセキュリティや生物学などの領域で依頼を断るものがあります。fallbacks:に代替モデルを並べると、断られたリクエストをAPIが同じリクエストの中で順に再試行します。
ClaudeLanguageModel(name: .opus5_5, auth: auth, fallbacks: [.opus4_8])指定できるのは3つまでで、依頼したモデルが代替として認めるモデルに限ります。fallbacks: .serverDefaultなら、拒否された領域に推奨される代替をAPIが選びます。推奨が無い領域では、拒否がそのまま返ります。回答の途中で断られた場合、代替モデルは断ったモデルの文章の続きから書きます。応答には1つの回答として読める形で返り、受け渡しの位置は空のテキストセグメントで示されます。
for case .response(let entry) in session.transcript {
if let handover = entry.claudeHandovers.last {
print("\(handover.fromModelID) declined, and \(handover.toModelID) finished the answer")
}
}代替が動いた後は、約1時間のあいだ会話の次のリクエストが代替モデルへ直接向かい、その応答に受け渡しはありません。どのモデルが答えたかはentry.claudeModelIDで見ます。各代替モデルには、そのモデルが受け付ける思考とeffortが渡されます。fixedEffort:を使うときは、各代替モデルが受け付ける最も近い水準になります。スキーマや画像は、チェーン内の全モデルが対応していないと使えません。サンプリングパラメータも全モデルが受け付けるときだけ送られます。session.usageが数えるのは、断られた試行ではなく、実際に応答したモデルのトークンです。.proxiedのときは、プロキシがanthropic-betaヘッダーを転送する必要があります。
もう1つは、ユーザー単位のプロファイルです。アプリがユーザーの代わりにリクエストを出すなら、バックエンドがAPIの/v1/user_profilesでユーザーごとにプロファイルを作り、そのIDをアプリへ渡します。アプリはモデル生成時に渡します。
ClaudeLanguageModel(name: .opus5_5, auth: auth, userProfileID: profileID)IDはanthropic-user-profile-idヘッダーに、user-profiles-2026-08-18のベータ指定と共に全リクエストで送られます。未知のIDはリクエストごと失敗します。リクエストはそのユーザーのプロファイルに紐づきます。.proxiedでは、プロキシが2つのヘッダーとも転送します。
画像入力とエラーハンドリング
画像入力に対応したモデルは、フレームワークの標準的なvision機能をそのまま宣言します。画像コンテンツはフレームワークの通常のセッションAPIで渡せば、パッケージ側がClaude APIの画像フォーマットに変換します。渡し方を変える必要はありません。
エラーは、フレームワークの型に対応するものがあればそこへマップされます。コンテキストウィンドウの超過は.contextSizeExceeded、HTTP 429は.rateLimited、設定した時間を超えたリクエストは.timeoutです。対応が無いものはClaudeErrorとして表面化します。パッケージのソースでは、HTTP 529(overloaded)も.rateLimitedに入り、リクエストサイズの超過も.contextSizeExceededになります。
do {
let response = try await session.respond(to: prompt)
print(response.content)
} catch ClaudeError.missingCredential {
// APIキーの入力を促す
} catch let error as LanguageModelError {
// レート制限・ガードレール・コンテキスト長・デコードなど、フレームワーク側の想定内エラー
} catch {
// 通信エラー
}.rateLimitedを捕まえたら、そのターンだけSystemLanguageModel(Appleのオンデバイスモデル)にフォールバックできます。リクエストをキューに積む、リトライを促すUIを出す、といった対応も実用的です。
よくあるつまずき
- ストリーミングの各要素を差分だと思って処理する。
streamResponse(to:)が返す各要素は、その時点までの累積スナップショットです。差分だと仮定して結合すると内容が重複します。 - シミュレーターで
.appAttestを試す。Secure Enclaveの無い環境ではApp Attestが動かず、ClaudeError.attestationUnsupportedになります。シミュレーターでは.apiKeyを使います。 - ドキュメントとREADMEの書き方の違いに戸惑う。ドメイン絞り込みの引数名のように、ドキュメントとリポジトリのREADMEで形が違う箇所があります。READMEは、認証方式を「
.appAttestを推奨、開発でも本番でも使える」と書く一方、ドキュメントは開発を.apiKey、実機を.appAttestとしています。シミュレーターで動かすなら.apiKeyが必要、という点は両方で共通です。 - プロンプトキャッシュのTTLやブレークポイント位置を調整しようとする。このパッケージはプロンプトキャッシュを自動適用しますが、TTLやブレークポイントの位置は設定できません。細かい制御が要る場合はネイティブのMessages APIを直接使う設計に切り替えます。
まとめ
Claude for Foundation Modelsは、Appleの標準的なセッションAPIを崩さずにClaudeを呼べる橋渡し役です。導入自体はClaudeLanguageModelを渡すだけで動きます。決めるべきは認証方式と、オンデバイスモデルへ戻す条件の2点です。ストップシーケンスやバッチ処理、Files API、トークンカウント、任意のベータヘッダーを自分で付けることのように、フレームワークのプロトコルで表現できない機能は、このパッケージ経由では使えません。そうした機能が要る場面は、Anthropic APIをネイティブに呼ぶ構成に切り替えます。