App Extension 式 Screen Saver
App Extension で動く Screen Saver でしかできないこと:
ユーザーによる承認が必要なリソースにアクセスする (e.g. 写真ライブラリ)
スライドショー系のスクリーンセーバーは写真ライブラリにアクセスするために App Extension 式になっている
NS***UsageDescription 等は App Extension ではなくホストApp側に書く必要があることに注意
スクリーンセーバ 環境設定で、「スクリーンセーバのオプション…」ボタンを出さずに直に設定UIを出す
SIP を有効にした macOS で Xcode のデバッガを Screen Saver にアタッチする
ちなみに旧式も結局は App Extension でラップされている
/System/Library/Frameworks/ScreenSaver.framework/PlugIns/legacyScreenSaver.appex
App Sandbox に入っているとはいえかなり緩い entitlements が付いている
ので旧式 Screen Saver を作る際は通常気にする必要はない、はず
たぶん x86_64 しかないスクリーンセーバを Rosetta 2 に食わせるために別プロセスにする必要があった?
App Extension 版 Screen Saver の作り方:
---
適当に生贄にする App Extension を作る
App Extension の Info.plist を以下のように書き換える:
NSExtension 内の
NSExtensionPointIdentifier を com.apple.screensaver に
NSExtensionPrincipalClass を ScreenSaverExtension のサブクラス名に
とはいえここでやることもそんなにないので直で ScreenSaverExtension って書いてもいいかも
ScreenSaverViewControllerClass を ScreenSaverViewController のサブクラス名に
ScreenSaverConfigurationViewControllerClass を ScreenSaverConfigurationViewController のサブクラス名に
設定要素がないならなくてもいい?
旧式と同じように ScreenSaverView のsubclassをゴリゴリ書く
ScreenSaverView は公開APIだが、hasConfigureSheet あたりは設定しても影響がないことに注意
どうにかして以下のクラス (Private API) のクラス定義を用意する
ScreenSaverViewController
ScreenSaverConfigurationViewController
ScreenSaverExtension
真面目に class-dump とかしなくても それぞれ NSViewController / NSObject のサブクラスにしておけば実用上あまり問題はないはず ScreenSaverViewControllerClassで指定したクラスでは
loadView() で view にさっき作った ScreenSaverView の subclass を指定する必要がある
ウワサ: 独自イニシャライザが発生するようなこと(e.g. subclass側でプロパティを足すとか)をすると死ぬらしい by banjun.icon
ScreenSaverConfigurationViewControllerClass で指定したクラスでは
loadView() でviewにいい感じに設定項目を置いておく
これでできるはず
デバッグについて
ただし前述の通り view hierarchy は見れない
システム環境設定.app 内のプレビューではデバッグができないので、Xcodeでデバッグしたいなら上に書いた方法しかない XPCのプロセスが二重になる (「デスクトップとスクリーンセーバー」設定がXPC)とXcodeの App Extension デバッガが追えない?らしくて自動でアタッチしてくれない
あと旧式でも App Extension 式でも使える方法として、適当にアプリtargetを作ってScreenSaverViewのサブクラスを貼ってしまうという手がある