← すべての記事

macOS 27、他チームのコンテナへのアクセスを確認なしで拒否

macOSでは、エンタイトルメント(entitlement)を持たないApp Groupに対しても、containerURL(forSecurityApplicationGroupIdentifier:)は一見正しいURLを返します。Appleはこれを明記しています。iOSでは無効な識別子に対してnilを返す一方、macOSでは「App Groupが無効であっても、常に期待される形式のURLが返される」とされています。4 macOS 27では、この挙動が新しい制限とぶつかります。

macOS 27は、他チームのコンテナへのアクセスを拒否する前にユーザーへ確認しなくなりました。 別の開発者チームのAppデータコンテナやApp Groupコンテナ内のファイルを読む際、これまでは認可を求めるダイアログが出ていました。それが既定で失敗するようになり、復旧できるのはユーザーが「プライバシーとセキュリティ」の中の該当項目を見つけた場合だけです。1

この2つの事実が組み合わさると、失敗地点に何の手がかりも残らない障害になります。ダイアログは出ません。メソッドはnilではなくURLを返します。パスは想定どおりに見えます。拒否が表に出るのは後になってから、つまりファイル操作の時点であり、そこでは「ファイルが見つからない」ように見えるのです。

TL;DR

macOS 27は、他チームのAppデータコンテナおよびApp Groupコンテナへのアクセスに関するユーザー認可のダイアログを廃止し、そうしたアクセスを既定で拒否します。ユーザー側の制御は「プライバシーとセキュリティ」設定へ移りました。1 この変更はSystem Integrity Protectionの項目に、バグ修正ではなく新機能として記載されています。App Groupコンテナに関するApple自身のガイドは、macOS 27が廃止したダイアログの挙動を今なお説明したままです。macOS版のAPIはアクセスできないグループに対しても整った形式のURLを返すため、拒否が現れるのはAPIの呼び出し時ではなく読み取り時になります。同一チーム内のアクセスは影響を受けません。境界となるのはTeam IDです。

何が変わったのか

macOS 27のリリースノートには、System Integrity Protectionの下に一文だけ記載があります。1

「他の開発者チームのAppデータコンテナおよびApp Groupコンテナ内のファイルへのアクセスは、ユーザーに認可を求めなくなりました。こうしたアクセスは既定で拒否され、ユーザーは『プライバシーとセキュリティ』設定で管理できます。」

Radar 161835690。2つの節に、2つの別々の変更が含まれています。

前半はダイアログの廃止、後半は拒否の既定化です。この変更を扱う記事は拒否のほうを見出しに立てがちですが、そちらは2つのうち面白みの薄い半分です。既定を厳しくするのは、よくある話にすぎません。一方、消えたダイアログは、ユーザーが経験する障害の形も、あなたの手元に届くバグレポートの形も変えてしまいます。

以前なら、ユーザーはダイアログを見て判断を下しました。ユーザーが拒否すれば、人間が選んだ結果だとアプリの側にも分かりました。今は誰にも尋ねられません。アクセスは単に起こらず、有効化への唯一の道は、誰かに教えられない限り訪れる理由のない設定画面の中にあります。

Appleのドキュメントが今も説明している、以前の挙動

この制限は、2つ前のリリースで導入された保護の上に乗っています。AppleのApp Groupコンテナのガイドにはこうあります。2

「macOS 15以降では、Appサンドボックスの機能を持たないAppであっても、App GroupコンテナがAppのローカルファイルに[System Integrity Protection]を提供します。これらのApp Groupコンテナは、App Groupに属さないAppからのアクセスを制限します。App Groupに属さないAppがApp GroupまたはAppデータコンテナ内の場所へアクセスしようとすると、認可を求めるユーザーダイアログが表示されます。」

このページはダイアログについて現在形で書かれています。本稿執筆時点で、AppleはmacOS 27向けに更新していません。

実務上の帰結はこうです。この問題に突き当たった開発者がドキュメントを検索し、公式ページにたどり着くと、「認可ダイアログが表示される」と教えられます。実際には表示されません。そこで当然のように、ダイアログが壊れているのだ、あるいは自分のエンタイトルメントの設定が誤っているのだと結論づけ、見当違いの場所で時間を使うことになります。

このページの現状は、本稿のタイムスタンプを信じるのではなく、自分で確認する価値があります。ドキュメントはいずれ追いつきます。

なぜ失敗が遅れて現れるのか

方針変更をデバッグ問題に変えているのは、APIの挙動です。

containerURL(forSecurityApplicationGroupIdentifier:)のAppleリファレンスは、プラットフォーム間の差異を明確に述べています。4

「ファイルシステム上における、そのグループの共有ディレクトリの場所を示すURL。iOSでは、グループ識別子が無効な場合に値はnilとなります。macOSでは、App Groupが無効であっても常に期待される形式のURLが返されるため、使用を試みる前に、その下のディレクトリにアクセスできるかどうかを必ずテストしてください。」

解説セクションも同じ警告を繰り返しています。エンタイトルメントを持たないグループ識別子でメソッドを呼び出しても期待される形式のURLは返るものの、ディレクトリは存在せず、サンドボックス化されたAppではそれを作成できません。4

つまり、よくある防御的な書き方はここでは何の役にも立ちません。

// This guard passes on macOS regardless of entitlement.
guard let url = FileManager.default.containerURL(
    forSecurityApplicationGroupIdentifier: "ABCDE12345.com.example.shared"
) else {
    return  // never taken on macOS
}

URLは整った形式です。指し示す先は~/Library/Group Containers/<team>.<group>、まさにそのコンテナが置かれるはずの場所です。ファイル操作を実行するまで、すべてが成功に見えます。

正しい対処は、成功するかどうかを事前に尋ねるのではなく、実際に読みに行って失敗を処理することです。

let fm = FileManager.default
guard let url = fm.containerURL(
    forSecurityApplicationGroupIdentifier: groupID
) else { return }  // macOS never takes this branch

do {
    _ = try fm.contentsOfDirectory(atPath: url.path)
    // Reached the container.
} catch {
    // On macOS 27 a cross-team denial lands here.
    // No prompt fired. The user was never asked.
    presentContainerUnavailable(error)
}

事前チェックとしてisReadableFile(atPath:)に手を伸ばしたくなりますが、こらえてください。Appleはこの種のテスト全般を避けるよう助言しています。3

「ファイルシステムの現在の状態や、ファイルシステム上の特定のファイルの状態にもとづいて振る舞いを決めようとすることは推奨されません。そうすると奇妙な挙動や競合状態を招きかねません。操作が成功するかどうかを事前に見極めようとするよりも、実際に操作(ファイルの読み込みやディレクトリの作成など)を試み、エラーを確認し、それを適切に処理するほうがはるかに優れています。」

今回の変更に固有の、もう1つの理由もあります。同じページには、isReadableFile(atPath:)が読み取り可否の判定に「実ユーザーIDと実グループIDを使う」と書かれています。3 これはPOSIXパーミッションの評価です。他チームのコンテナに対する拒否はPOSIXの上に重ねられた方針上の判断であり、パーミッションビットの確認がそれを反映する保証はありません。事前チェックがtrueを返したのに読み取りは失敗する——これは事前チェックがないよりも悪い状態です。驚きの発生点を、原因からさらに遠ざけてしまうからです。

iOS 27でcanOpenURLが非推奨にを読んだ方は、同じ形に見覚えがあるはずです。Appleは事前に尋ねる手段を次々と取り去り、試す手段だけを残していきます。「試して処理する」は、1つのAPIに対する回避策ではなく、汎用的な答えになりつつあります。

プラットフォーム間の非対称性にも注目してください。iOSはnilを返し、呼び出し地点で正直に失敗します。macOSはURLを返し、失敗を先送りします。macOS 27の変更は、そのダイアログ——2つのうち元から静かだったほうのプラットフォームに残された、唯一の手がかり——を取り除いたわけです。

もう1つ罠があります。Appleの指針では、~/Library/Group Containers/...を手で組み立てるのではなく、このメソッドが返すURLを常に使うよう求めています。将来のリリースで場所が変わる可能性があるためです。4 そのパスをハードコードしていた人には、確認すべきメソッド呼び出しがそもそも存在せず、読み取り可否のテストを置く場所もありません。

おそらくSDKによる限定ではありません

自然に浮かぶ疑問は、古いSDKに留まればこの変更を先送りできるのか、というものです。リリースノートは何も言っていません。

ただ、そこに現れているパターンはあります。macOS 27のノートは、11か所の項目で明示的にSDKを条件とする言い回しを使っています。「macOS 27.0のSDKでビルドされたAppでは」「27.0のSDKでビルドされたAppでは」「プロジェクトの最小デプロイメントターゲットが27.0未満の場合」。1 条件が当てはまるとき、Appleはきちんと限定を付けるのです。

コンテナの項目にはそうした限定がありません。この不在が意図的だと読むのが妥当であれば、この制限はどのSDKでビルドされたかに関わらず、macOS 27上で動作するすべてのバイナリに適用されるOSレベルの方針ということになります。

とはいえ、Appleが直接そう述べたわけではないので、あくまで有力な推論として扱ってください。いずれにせよ実務上の結論は同じです。古いSDKを緩和策として当てにしないこと。アクセスは失敗する前提で設計しましょう。

実際に影響を受けるのは誰か

同一チーム内のアクセスは無傷です。AppleのルールはTeam IDという境界を偶発的なものではなく構造的なものにしています。「異なる開発者チームが同じApp Groupを使うことはできません」とされる一方、1つのチームは自社のApp群と補助プロセスの間でグループを共有できます。2

壊れるのは、その線をまたごうとするAppです。

移行・インポートツール。 競合製品や前世代製品のコンテナを読み、ユーザーデータを取り込むもの。これが最も分かりやすいケースです。以前からダイアログを挟む形で制限されていましたが、ユーザーが承認することもありました。

バックアップ・同期ユーティリティ。 Appコンテナを列挙してバックアップするツールは、自チーム以外のものを黙って飛ばすようになります。

所属が変わったコンパニオンApp。 最も鋭いケースです。コードは何ひとつ変わらないからです。2つのAppが1つのTeam IDの下で配布され、問題なくグループを共有しているとします。そこへ買収、チーム分割、あるいは別の開発者アカウントへの移行が起きると、両者は別々のTeam IDの下に置かれます。App Group識別子は正しく見えたまま、URLも解決したまま、データだけが届かなくなるのです。

この最後のケースは掘り下げる価値があります。タイミングが不利に働くからです。Team IDの変更は署名と配布を管理する担当者が扱いますが、共有コンテナへの依存はその立場からはたいてい見えません。コードはコンパイルされ、両方のAppは出荷されます。どちらのテストスイートも捕まえられません。ユニットテストは実際の他チームコンテナを触りませんし、CIはビルドマシンが持つ何らかの識別情報の下で両ターゲットを動かすからです。障害は本番環境の、ユーザーのマシン上で現れます。ユーザーが同じ製品だと当然のように考えている2つのApp——その間で、データが同期されなくなるという形で。

Team IDの移行を計画しているなら、監査は機械的にできます。各ターゲットが宣言しているApp Group識別子をすべてgrepし、それぞれについて、どのバンドルが読んでいるかを列挙します。異なるTeam IDに分かれることになるバンドルから読まれている識別子は、すべて破綻します。移行前なら10分の確認で済み、移行後にはサポート案件になります。

AppleはApp Groupに参加できる対象を挙げています。バンドル構造内のメイン実行ファイル、App Extension、App Clip、そしてXPC Serviceです。2 そのどれもが、この問題が表面化しうる場所です。

切り分け方

コードのバグだと決めつける前に、確認しておく価値のあることが2つあります。

まず、システムがそもそもエンタイトルメントを検証したかを確かめます。Appleは、実行中のプロセスに対するentitlements-validatedフラグの実行時確認方法を示しています。2

sudo launchctl procinfo <pid>

App Groupの認可が存在する前に作成されたプロビジョニングプロファイルには、それが含まれていない場合があります。すると、macOS 27の拒否とまったく同じに見えるアクセス失敗が、別の原因で起こります。「Automatically manage signing」が有効で、ビルド設定REGISTER_APP_GROUPSYesになっていれば、Xcodeがプロファイルを更新します。2

次に、どの識別子の書式を使っているかを把握します。group.で始まるグループは、Appのプロビジョニングプロファイルに含まれている必要があります。<TeamID>.<group name>の形式はプロファイルを必要としません。システムが署名IDに対してチームIDの接頭辞を検証するからです。ただしこの形式はmacOS専用で、Keychain Access Groupsでは使えません。2

そのうえで「プライバシーとセキュリティ」を確認しましょう。リリースノートによれば、ユーザーの制御はそこに移ったのですから。1

尋ねられない拒否のために設計する

あのダイアログは、セキュリティの仕事だけでなく、プロダクトの仕事もしていました。判断すべきことがあるとユーザーに伝え、判断が下されたことをアプリに伝えていたのです。その両方が、これからはあなたの責任です。

境界で、一度だけ試す。 同期ループの途中で拒否に気づくのではなく、コンテナを解決した最初の時点で軽い読み取りを1回試みましょう。キューの奥深くで失敗すると、結果は中途半端になり、たまたま次に来たファイルのせいにされたエラーだけが残ります。解決の時点で一度試せば、分岐する場所は1か所で済みます。しかもパーミッションの事前チェックと違い、実際の読み取りと同じ経路を通ります。

何が起きたかを、ユーザーの言葉で伝える。 「データを読み込めませんでした」では、データ消失に関するサポート問い合わせを招くだけです。正確なメッセージは、境界と対処の両方を名指しします。そのデータは別の開発者のAppに属していること、macOSは既定でそのアクセスをブロックすること、スイッチは「プライバシーとセキュリティ」にあること。場所の分からない失敗に対して、ユーザーは動きようがありません。

リトライしない。 拒否は一時的なエラーではなく、方針上の状態です。ブロックされたコンテナに対するバックオフのループは、何も変えないままバッテリーを消費しログを埋めます。一度失敗したら状態を提示し、設定を変えたあとにユーザー自身が再確認を起動できるようにしましょう。

ポーリングではなく、アクティブ化のタイミングで再確認する。 ユーザーが設定を変えに行って戻ってきたとき——それがアクセスを再テストすべき瞬間です。ブロックされたパスをタイマーでポーリングしても、選んだ間隔ごとに同じ拒否が返ってくるだけです。

そもそも他チームの読み取りが必要かを問い直す。 これは居心地の悪い問いですが、たいていはこれが正解です。競合製品のコンテナを読む移行ツールは、プラットフォームが3リリースにわたって狭めてきたことをやろうとしています。macOS 15でサンドボックス化され、ダイアログが挟まり、そして今回の拒否。相手側のAppが提供するエクスポート経路、ユーザーがファイル選択画面で操作するドキュメントベースのインポート、文書化された交換フォーマット——いずれもこの変更を生き延びます。Team IDの境界をまたぐコンテナ読み取りには明確な方向性があり、その矢印は一方向を指しています。

ファイル選択画面については個別に触れておく価値があります。プラットフォームが意図している逃げ道がこれだからです。ユーザーがファイルを選ぶという行為は、セキュリティスコープ付きの仕組みを通じて明示的にアクセスを与えます。消えてしまったあのダイアログよりも強い許可の筋道であり、しかも設定変更なしに今日から機能します。

要点

macOSアプリ開発者へ: - macOSでは、containerURL(forSecurityApplicationGroupIdentifier:)nil以外を返したことを、アクセスできる証拠と見なさないこと。常にURLは返ります。実際に読みに行き、エラーを処理しましょう。 - 事前チェックとしてのisReadableFile(atPath:)は避けること。Appleはファイルシステムの結果を予測しないよう助言しており、このメソッドはPOSIXパーミッションを評価しますが、方針レイヤーの拒否はそこに触れるとは限りません。 - 異なるTeam IDに属するコンテナを読むコード経路をすべて洗い出すこと。macOS 27では、誰にも尋ねることなく失敗します。 - ~/Library/Group Containers/...をハードコードしているなら、守るべきメソッド呼び出しがありません。確認を置ける場所を作るため、APIに移行しましょう。

移行ツールやバックアップツールを出しているチームへ: - 他チームの読み取りは、もうダイアログ1つ先にはありません。拒否を既定の状態として設計し、設定の場所をユーザーに伝えましょう。 - 障害はエラーではなく、データが存在しないという形で現れます。明示的なメッセージを追加してください。そうしないと、ユーザーはデータ消失として報告してきます。

Team IDを変更する人へ: - 買収やアカウント分割は、以前は同じTeam IDの接頭辞を共有していたApp間のApp Group共有を、静かに断ち切ります。ソースコードには何の兆候も現れません。

FAQ

1つの開発者アカウント内でコンテナを共有しているAppには影響しますか?

いいえ。境界となるのはTeam IDです。Appleのルールはもともと異なるチームが同じApp Groupを使うことを認めておらず、1つのチームは自社のApp、App Extension、App Clip、XPC Serviceの間でグループを共有できます。2

ユーザーが承認できるダイアログは表示されますか?

macOS 27では表示されません。リリースノートには、これらのアクセスはもはや認可を求めず、既定で拒否され、管理は「プライバシーとセキュリティ」設定に移ったと明記されています。1

古いSDKでビルドすれば回避できますか?

おそらく無理です。リリースノートは他の11件の変更について明示的にSDKを条件とする言い回しを使っていますが、この項目には使っていません。これはすべてのバイナリに適用されるOSレベルの方針であることを示唆します。1 Appleが明言しているわけではないので、有力な推論として扱い、アクセスは失敗する前提で計画してください。

プロビジョニングの問題と、どう見分ければよいですか?

sudo launchctl procinfo <pid>を実行し、システムがプロセスにentitlements-validatedフラグを立てているかを確認してください。古いプロビジョニングプロファイルはApp Groupの認可より前のもので、無関係な原因から、よく似た見た目の失敗を起こすことがあります。2

なぜドキュメントは今もダイアログについて書いているのですか?

AppleのApp Groupコンテナのガイドは、macOS 15の挙動を説明したもので、本稿執筆時点ではmacOS 27の変更に合わせて更新されていません。2 このページの現状は、本稿の日付に頼るのではなく確認してください。

出典


  1. Apple, “macOS 27 Golden Gate Beta 4 Release Notes.” System Integrity Protection, New Features: “Accessing files in other developer teams’ app data containers and app group containers no longer prompts the user for authorization; such accesses are denied by default and can be managed by the user in Privacy & Security settings.” Radar 161835690。11件の他項目で使われ、この項目には見られないSDK限定の言い回しについても、同じ出典。HTMLはクライアント側でレンダリングされます。機械可読版はdeveloper.apple.com/tutorials/data/documentation/macos-release-notes/macos-27-release-notes.jsonにあります。 

  2. Apple, “Accessing app group containers in your existing macOS app.” macOS 15のダイアログの挙動、異なる開発者チームがApp Groupを共有できないというルール、group.<TeamID>.<group name>の識別子の違い、sudo launchctl procinfoによるentitlements-validatedの確認、ビルド設定REGISTER_APP_GROUPS、およびコンテナに参加できる対象の一覧の出典。2026年8月1日取得、依然としてダイアログについて記述あり。 

  3. Apple, “isReadableFile(atPath:).” ファイルシステムの状態を予測することへの警告の出典: “It’s far better to attempt an operation (such as loading a file or creating a directory), check for errors, and handle those errors gracefully than it is to try to figure out ahead of time whether the operation will succeed.” また、このメソッドが「ファイルが読み取り可能かを判定するために、実効ユーザーIDおよび実効グループIDではなく、実ユーザーIDと実グループIDを使う」という記述の出典でもあり、これがPOSIXレベルの確認を方針レイヤーの拒否の代理指標として信頼できない理由です。 

  4. Apple, “containerURL(forSecurityApplicationGroupIdentifier:).” 戻り値: “In iOS, the value is nil when the group identifier is invalid. In macOS, a URL of the expected form is always returned, even if the app group is invalid, so be sure to test that you can access the underlying directory before attempting to use it.” コンテナのパスを手動で組み立てることへの警告の出典でもあります。 

関連記事

macOS 27とiPadOS 27でメニュー項目の画像が消える

macOS 27とiPadOS 27は、メニュー項目の画像をデフォルトで非表示にします。何が消えるかは、どのSDKに対してリンクしたかで変わります。3つのフレームワーク、それぞれ異なる対処法。

2 分で読める

Appleのフォントインタプリタが Swift 化、しかも13%高速に

Appleのセキュリティチームは、TrueType ヒンティングインタプリタを C からメモリ安全な Swift へ書き換え、13%高速化したうえでオープンソース化し、その手法まで公開しました。

2 分で読める

The Mac App Store Won't Let Window Managers Exist. I Shipped One Anyway.

App Sandbox forbids the API every window tiler needs, and Apple said ship outside the store. 941 Tiles ships inside it: …

8 分で読める