logo hsb.horse
← ブログ一覧に戻る

ブログ

iOS Shield Extension は1本ではなく2本必要という話

ManagedSettingsでブロックを設定したとき表示されるシールド画面は、Action ExtensionとConfiguration Extensionの2つの別ターゲットで成り立っている。正しいNSExtensionPointIdentifierとプリンシパルクラスを整理した。

公開日:

ManagedSettings でブロックルールを設定すると、対象サイトへのアクセス時にシールド画面が表示される。このシールドをカスタマイズしようとして、iOS Extension を初めて触る人が必ずはまるポイントがある。Extension を1つ追加すればいいと思っていると詰まる。必要なターゲットは2本だ。

そもそも iOS の Extension とは、メインアプリとは別に動く小さなプロセスのことで、Xcode ではターゲットを追加することで作成する。Shield Extension はシールド画面専用の Extension で、メインアプリとは独立して動く。

Shield Action Extension

ユーザーがシールド画面上のボタンを押したときの処理を担う。プリンシパルクラス——Extension のエントリポイントになるクラス——は ShieldActionDelegate を継承する。

import ManagedSettings
class ShieldActionExtension: ShieldActionDelegate {
override func handle(
action: ShieldAction,
for application: ApplicationToken,
completionHandler: @escaping (ShieldActionResponse) -> Void
) {
completionHandler(.close)
}
}

Extension Point Identifier は com.apple.ManagedSettings.shield-action-service だ。

フレームワークは ManagedSettings を import する(ManagedSettingsUI ではない)。この混同が起きやすいので注意が必要だ。

Shield Configuration Extension

シールド画面のビジュアルをカスタマイズする。タイトル、本文、ボタンラベルをアプリ側の文脈に合わせた内容にしたいときに使う。プリンシパルクラスは ShieldConfigurationDataSource を継承する。

import ManagedSettingsUI
import ManagedSettings
class ShieldConfigurationExtension: ShieldConfigurationDataSource {
override func configuration(
shielding application: Application
) -> ShieldConfiguration {
return ShieldConfiguration(
backgroundBlurStyle: .systemUltraThinMaterial,
title: ShieldConfiguration.Label(text: "集中モード中です", color: .label)
)
}
}

Extension Point Identifier は com.apple.ManagedSettingsUI.shield-configuration-service だ。こちらは ManagedSettingsUI を import する。

Info.plist の設定

それぞれ別ターゲットの Info.plist に、正しい Extension Point Identifier を設定する。

Action Extension の場合:

<key>NSExtensionPointIdentifier</key>
<string>com.apple.ManagedSettings.shield-action-service</string>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).ShieldActionExtension</string>

Configuration Extension の場合:

<key>NSExtensionPointIdentifier</key>
<string>com.apple.ManagedSettingsUI.shield-configuration-service</string>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).ShieldConfigurationExtension</string>

App Group の設定を忘れない

2本の Extension とメインアプリは、同じ App Group を共有する必要がある。App Group とは、複数のターゲット間でデータを共有するための仕組みだ。iOS では通常アプリ同士がデータを直接やり取りできないが、同じ App Group に所属していれば共有コンテナを使ってデータを読み書きできる。

Xcode の Signing & Capabilities でそれぞれのターゲットに App Groups を追加し、同一のグループ ID を設定する。メインアプリ、Action Extension、Configuration Extension の3つすべてに設定が必要だ。iOS 開発が初めてだと「設定したつもりが1つ抜けていた」というミスが起きやすい。1つでも欠けると実機でデータが共有されない。

シールド画面から何ができるか

ShieldActionDelegate が返せる値は .close.defer の2つだ。.close はシールドを閉じてブロックを維持する。.defer は追加のユーザー操作をトリガーするために使う。

「シールドからメインアプリを直接開いてアンロック画面を表示する」という動きは、このインターフェースからは直接実現できない。これも iOS 開発を始めたばかりだと「なぜできないのか」がわかりにくい部分だ。その実装パターンについては別の記事に分けた。