こんにちは、エンジニアのオオバです。
Finder のツールバーや右クリックから、「名称未設定.txt」「名称未設定.md」を作れる Finder Sync 拡張を自作しています。
これを Mac App Store に出そうとして、サンドボックス化でハマりました。
設計段階では「ホストアプリで作った bookmark を App Group で拡張に渡せば済む」と考えていたんです。
実機で試すと、その案は成り立ちませんでした。
bookmarkの共有は諦め、拡張からURLスキームでホストアプリを裏で起動して、ファイル作成をホストアプリに任せる
先に結論をまとめます。
①ホストアプリで作ったapp-scopedのbookmarkは、App Groupで渡しても拡張では解決できない(NSCocoaErrorDomain Code=259)
②document-scopedのbookmarkはフォルダを対象にできない(Item URL disallowed by security policy)
③拡張はURLスキームで依頼を送るだけにして、ファイル作成はフォルダ許可を持つホストアプリが行う
ここから、試した順に書いていきます。
→11万文字で徹底解説した「DOTweenの教科書」Unityアニメーションの超効率化ツールはこちら
やりたかったこと:absolute-pathのtemporary-exceptionを外す
拡張には、これまで次の entitlement を付けていました。
com.apple.security.temporary-exception.files.absolute-path.read-write(対象は/)
ルート以下すべてに読み書きできる例外です。
開発中は便利でした。
とはいえ、このまま審査に出せばほぼ確実に却下されます。
App Store に出すなら、この例外を外して、ユーザーが許可したフォルダにだけ ファイルを作れる形にする必要がありました。
最初の案:bookmarkをApp Groupで拡張に渡す
最初に考えた流れは次のとおりです。
- ホストアプリで
NSOpenPanelを出し、ユーザーにホームフォルダを許可してもらう - 許可されたフォルダから security-scoped bookmark を作る
- App Group の
UserDefaults(suiteName指定)に保存して、拡張と共有する - 拡張側で bookmark を解決し、
startAccessingSecurityScopedResourceを呼んでから書き込む
ホストアプリの設定画面は、この案を前提に作りました。

画面は3つの手順に分かれています。
- ファイルを作るフォルダを許可する(ホームを許可すればデスクトップ・書類・iCloud Drive を含むホーム配下で使える。外付けドライブは「フォルダを追加」で個別に許可)
- システム設定で Finder 機能拡張を有効にする
- Finder のツールバーにボタンを置く
App Groupでの共有自体は動いた
App Group の共有は、ad-hoc 署名でも動きました。
拡張から bookmark の Data はちゃんと読めています。
ここまでは順調でした。
拡張でbookmarkを解決できない
問題は拡張で bookmark を解決するところです。
次のエラーで失敗しました。
NSCocoaErrorDomain Code=259
「ファイルのフォーマットが正しくないため、開けませんでした。」
データは届いているのに、フォーマットが正しくないと言われる。
app-scoped bookmark は、作ったアプリでしか解決できません。
ホストアプリと拡張は別のプロセスで、サンドボックス上も別のアプリとして扱われます。
そのため、ホストアプリが作った bookmark を拡張が解決することはできませんでした。
次の案:document-scoped bookmarkも駄目だった
app-scoped が駄目なら、document-scoped bookmark はどうか。
App Group のコンテナ内に置いたファイルを基準にして、document-scoped bookmark を作る方法を試しました。
まず出たのが次のエラーです。
Required entitlement com.apple.security.files.bookmarks.collection-scope is missing or false
エラー文のとおり com.apple.security.files.bookmarks.collection-scope を付けると、今度は別のエラーに変わりました。
Item URL disallowed by security policy
document-scoped bookmark は、フォルダを対象にできません。
今回欲しいのは「ホームフォルダ以下に書き込む権限」なので、この案も使えませんでした。
サンドボックスは正しく効いている
念のため、拡張から直接ファイルを書き込めるかも確かめました。
結果は全フォルダで失敗。
temporary-exception を外したことで、サンドボックスは意図どおりに効いています。
つまり、拡張の中だけでファイルを作る道はふさがっている状態でした。
解決のヒント:OSSのRClickの作り
行き詰まったので、同じように Finder の右クリックを拡張する OSS の RClick(GitHub で1078スター)を読みました。
RClick では、拡張はクリックされたことを本体アプリへ伝えるだけでした。
ファイル操作は、常駐している本体アプリが行っています。
拡張から本体アプリへの連絡には DistributedNotificationCenter を使っていました。
本体アプリならフォルダ許可の bookmark を自分で作って自分で解決できるので、Code=259 の問題は起きません。
常駐はさせたくなかった
ただ、「新規ファイルを作る」だけのツールのために、本体アプリを常駐させるのは避けたいところ。
そこで、拡張から URL スキームでホストアプリへ依頼を送り、必要なときだけホストアプリを起動する方式にしました。
解決:URLスキームでホストアプリを裏で起動する
拡張からは、次のような URL をホストアプリへ送ります。
findernewfile://create?dir=...&ext=md&token=...
dir: ファイルを作るフォルダext: 拡張子(txtかmd)token: 合言葉(後述)
前面に出さずに起動する
拡張からは NSWorkspace の open(_:withApplicationAt:configuration:) で、URL とホストアプリを指定して開きます。
configuration は activates = false にしました。
これで、ホストアプリが前面に出ずに起動します。
URLで起動されたときは画面を出さない
ホストアプリは LSUIElement で起動するようにしています。
起動時の通知に入っている launchIsDefaultUserInfoKey を見て、起動のされ方を分けました。
false(URL で起動された)なら画面を出さず、ファイルを作るだけ- 普通にアプリを開いたときだけ
setActivationPolicy(.regular)にして、設定画面を出す
ユーザーから見ると、Finder のボタンを押したらファイルができるだけです。
すぐ終了すると続けて送った依頼を取りこぼした
最初は、1件処理したらすぐホストアプリを終了するようにしていました。
ところが、続けてボタンを押すと2件目以降の依頼を取りこぼしてしまう。
そこで、最後の依頼から30秒後に終了する ように変えました。
tokenで外からの呼び出しを弾く
URL スキームは、拡張以外からも呼べてしまいます。
たとえば Web ページのリンクに findernewfile:// を仕込まれると、勝手にファイルを作られるかもしれません。
その対策が token です。
App Group で拡張とホストアプリだけが知っている合言葉を共有し、URL の token が一致しない依頼は処理しないようにしました。
結果:主要な場所でファイルを作れた
この方式で、次の場所に新規ファイルを作れることを確認しました。
- ホームフォルダ
- iCloud のデスクトップ・書類
- iCloud Drive
- 外付けボリューム(ディスクイメージで確認)
拡張から absolute-path の temporary-exception を外したまま、これまでどおり使えています。
補足:設計前に実機で試すべきだった
今回いちばんの反省は、「bookmark を App Group で共有すればいい」という案を、確かめないまま設計の前提にしたことです。
設定画面まで作ってから、前提が崩れたことに気づきました。
Finder Sync 拡張とホストアプリの間では、この案は成り立ちません。
一方で、確かめるだけなら実機で数十分あれば足ります。
サンドボックスまわりは、ドキュメントを読んだだけでは分からない制約が多いですよね。
設計を固める前に、いちばん怪しいところだけ小さく試すのがおすすめです。
まとめ:Finder Sync拡張は依頼を送るだけにして、ファイル操作はホストアプリに任せる
①ホストアプリのapp-scoped bookmarkはApp Groupで渡しても拡張で解決できない(Code=259)
②document-scoped bookmarkはcollection-scopeを付けてもフォルダを対象にできない
③拡張からURLスキームでホストアプリを前面に出さず起動し、ファイル作成はホストアプリが行う
④続けて届く依頼に備えて、ホストアプリは最後の依頼から30秒後に終了する
⑤URLスキームにはApp Groupで共有したtokenを付けて、外からの呼び出しを弾く
Finder Sync 拡張を App Store に出そうとしている人の参考になれば嬉しいです。

この記事が気に入ったらフォローしよう
- macOS 15.4.1 / Xcode 16.2 / swiftc直ビルド・ad-hoc署名
