【macOS App】macOSアプリで特権モード(launchd)動作するヘルパーツールをビルドする

いつも細かい手順を忘れがちなので、macOSアプリで特権モードを利用する際の自分用メモ

#プロジェクト作成
プロジェクトを作成し、アプリ本体と特権モードを動かすヘルパーツールを登録。
アプリ本体は通常のAPP、ヘルパーツールはCommandLineToolなど。
AppStoreは使わないので署名は「Developer ID Application」で行う。

#追加したヘルパーツールに必要となるplistを追加。

```xml:apphelper.plist(ヘルパーツール本体)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleIdentifier</key>
	<string>com.sample.apphelper</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>apphelper</string>
	<key>CFBundleVersion</key>
	<string>1.0</string>
</dict>
</plist>
```

ヘルパーツールとのインタラクティブな制御は古典的プロセス間通信を利用。
そろそろXPC Service化してもいいのかもしれない。

```xml:apphelper-Launchd.plist(launchd用)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>ServiceIPC</key>
	<false/>
	<key>OnDemand</key>
	<false/>
	<key>Label</key>
	<string>com.sample.apphelper</string>
	<key>Disabled</key>
	<true/>
</dict>
</plist>
```

注意点

- `TARGETS->apphelper->TARGETS->apphelper->General->Identity->Identity`から`apphelper.plist`ファイルを登録しておく
そもそもデフォルトで用意してくれても良さそうだが、単に追加しただけでは存在しないらしい。

- Build Settingsの`Product build Identifier`と`ProductName`を一致させておく
これを見落としていて１日悩んだ...


#ターゲット登録
`appheler`をアプリ本体の`Target Membershiｐ`にチェック。
アプリ本体のBuild Phasesでヘルパーツールを関連づけ

- `Build Phases`->`Dependencies`にヘルパーツールを追加
- `Build Phases`->`Copy Files`に以下の設定を追加
    - `Copy Files`がない場合はPane左上の+ボタンから`New Copy Files Phase`しておく

```
Destination:Wrapper
Subpath:Contents/Library/LaunchServices
追加ボタン（+）を押してヘルパーツールを追加
```

ヘルパーツール側の設定
- `Build Settings`->`Other Linker Flags`に以下の４行を追加

```
-sectcreate
__TEXT
__launchd_plist
$(SRCROOT)/${TARGET_NAME}/apphelper-Launchd.plist
```

#ビルド（一回目）
この状態で一度プログラム全体をビルド。（これが重要）

#署名交換
アプリ本体とヘルパーツールを連携させるために、互いの署名を設定し合う。
SMJobBlessUtil.pyというツールがサンプルにあるのでこちらを利用する。

https://developer.apple.com/library/archive/samplecode/SMJobBless/Listings/SMJobBlessUtil_py.html#//apple_ref/doc/uid/DTS40010071-SMJobBlessUtil_py-DontLinkElementID_8

使い方は簡単

```
SMJobBlessUtil.py setreq <アプリファイル名> <アプリのplistファイル名> < ヘルパーツールのplistファイル名>
```

アプリ本体、ヘルパーツールにそれぞれ`Tools owned after installation`、`SMAuthorizedClients`が追加されたら成功。

```xml:Info.plist
anchor apple generic and identifier "<ヘルパーツールのbundleid>" and (certificate leaf[field.1.2.840.113635.100.6.1.9] /* exists */ or certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = "<AppID>")
```


```xml:apphelper.plist
anchor apple generic and identifier "<APPのbundleid>" and (certificate leaf[field.1.2.840.113635.100.6.1.9] /* exists */ or certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = "<AppID>")/* exists */
```

この状態で再度ビルドを行う。
コマンドやビルドが失敗した場合は以下を確認

- `Bundle identifier`と`Bundle Name`があっているかどうか
- 署名は適切か

さらに追加で、Sandboxが有効になっているとエラーになるので、entitlementsファイルで`App Sandbox:NO`としておくこと。

お疲れさまでした。