Notification Service Extensionは、プッシュ通知を受信した直後、ユーザーに表示される前にインターセプトするiOS拡張機能です。この拡張機能は、暗号化されたペイロードを復号化し、メディアファイルをダウンロードして添付し、通知のテキストやタイトルをリアルタイムで変更できます。Apple Developer Documentation(2025)によると、拡張機能を有効にするには、サーバーが通知属性にmutable-content:1キーを送信する必要があります。これがUNNotificationServiceExtensionを起動する唯一の条件です。
重要なポイント
Notification Service Extensionは、iOSのアプリ拡張機能で、デバイス側で受信したプッシュ通知をインターセプトし、ユーザーが表示する前にその内容を変更できます。これは、表示ではなくコンテンツを扱う唯一のタイプの通知拡張機能です。
Notification Content Extensionとの主な違い:Service Extensionは通知が表示される前に機能し、タイトル、本文、サウンドファイル、添付ファイルを変更できます。Content Extensionは表示された後に機能し、完成した通知の視覚的表現のみを管理します。これら2つの拡張機能は連携できます:Service Extensionが画像をダウンロードし、Content Extensionがカスタムインターフェースで表示します。
拡張機能は、apsディクショナリにmutable-content:1属性を持つプッシュ通知を受信すると自動的に有効化されます。iOSはバックグラウンドで拡張機能を起動し、元のUNNotificationRequestを渡し、表示用の変更バージョンを待機します。
UNNotificationServiceExtensionは、元のコンテンツとともに完全なUNNotificationRequestを受け取ります。拡張機能はUNNotificationContentの任意のフィールド(title、subtitle、body、userInfo、attachments、sound)を変更できます。変更は通知が表示される前に適用されます。
コンテンツの復号化 — プッシュ通知に暗号化されたペイロードが含まれている場合、拡張機能は表示前に復号化します。メディアのダウンロード — 画像やビデオを通知に添付します。ローカライゼーション — デバイスの地域設定に合わせて通知テキストを適応させます。データのエンリッチメント — ローカルストレージやキャッシュから追加情報を追加します。
Appleによると、アプリ間で最も一般的なシナリオは、リッチメディア通知のための画像のダウンロードです。サーバーはペイロードに画像URLを送信し、拡張機能は一時ディレクトリにダウンロードしてUNNotificationAttachmentを作成し、システムが標準またはカスタムインターフェースで表示します。
拡張機能は、通知テキストを完全に書き換えたり、タイトルを置き換えたり、サブタイトルを追加したりできます。例えば、メッセンジャーアプリは暗号化された通知を受信し、拡張機能で復号化して読み取り可能なテキストを表示できます。または、ニュースアプリは表示前にサブタイトルにニュースカテゴリを追加できます。
override func didReceive(
_ request: UNNotificationRequest,
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
) {
let content = request.content.mutableCopy()
as! UNMutableNotificationContent
if let imageURL = content.userInfo["media-url"]
as? String {
downloadAndAttach(imageURL: imageURL,
content: content,
handler: contentHandler)
}
}
UNNotificationServiceExtensionは、Service Extensionが継承するベースクラスです。このクラスは2つのライフサイクルメソッドを定義します:didReceive(_:withContentHandler:) — メインの処理メソッド、およびserviceExtensionTimeWillExpire() — タイムアウトハンドラーです。
didReceive(_:withContentHandler:)は、通知を受信したときに呼び出されます。拡張機能はUNNotificationRequestとcontentHandlerクロージャを受け取り、変更されたUNMutableNotificationContentを指定して呼び出す必要があります。拡張機能はcontentHandlerを呼び出す義務があります — 呼び出さない場合、iOSはタイムアウト後に元の通知を表示します。
重要:拡張機能は一度に1つの通知のみ処理できます。複数の通知が同時に届いた場合、iOSはそれぞれに個別の拡張機能インスタンスを作成します。順次処理のためにグローバル状態を使用することはできません。
serviceExtensionTimeWillExpire()は、残りの実行時間が切れそうになるとシステムによって呼び出されます。このメソッドでは、その時点で準備ができているコンテンツで直ちにcontentHandlerを呼び出す必要があります — メディアファイルのダウンロードが完了していなくても同様です。このメソッドでcontentHandlerを呼び出さないと、iOSは元の通知を表示します。
このメソッドでは、最小限許容可能なコンテンツを保存することをお勧めします — 例えば、テキストとタイトルはあるが、ダウンロードが間に合わなかった画像がない通知など。
UNNotificationAttachmentは、メディアファイルを通知に添付するために拡張機能によって作成されるオブジェクトです。拡張機能はネットワークからファイルをダウンロードし、一時ディレクトリに保存し、コンテンツタイプを指定してUNNotificationAttachmentを作成します。
UNNotificationAttachmentは、init(identifier:url:options:)イニシャライザを使用して作成されます。URLは拡張機能がアクセス可能な一時ディレクトリ内のローカルファイルを指す必要があります。作成後、添付ファイルはUNMutableNotificationContentのattachments配列に追加されます。
Appleはダウンロードにバックグラウンド設定のURLSessionを使用することを推奨しています — 標準のURLSessionを使用すると、ダウンロードがスレッドをブロックし、30秒の制限時間を消費します。バックグラウンドURLSessionは拡張機能が終了してもダウンロードを継続し、結果は次回起動時に使用できます。
サーバーが暗号化された通知を送信する場合、拡張機能はcontentHandlerを呼び出す前にペイロードを復号化する必要があります。復号化には通常、KeychainまたはApp Groupからキーを要求し、CommonCryptoで復号化し、通知のbodyまたはuserInfoを置き換えることが含まれます。復号化エラーの場合は、元のコンテンツでcontentHandlerを呼び出す必要があります — ユーザーが少なくとも通知が届いたことを確認できるようにするためです(読み取れなくても)。
func downloadAndAttach(
imageURL: String,
content: UNMutableNotificationContent,
handler: @escaping (UNNotificationContent) -> Void
) {
let task = URLSession.shared.dataTask(with:
URL(string: imageURL)!) { data, _, _ in
let url = FileManager.default
.temporaryDirectory
.appendingPathComponent("image.jpg")
try? data?.write(to: url)
let attachment = try? UNNotificationAttachment(
identifier: "image", url: url)
content.attachments = [attachment].compactMap { $0 }
handler(content)
}
task.resume()
}
Notification Service Extensionは厳格な時間制約の下で動作します。iOSは固定の実行時間を割り当てます — 有効化から約30秒です。この時間内に拡張機能がcontentHandlerを呼び出さなかった場合、システムは強制的にプロセスを終了し、変更されていない元の通知を表示します。
マルチレベルのフォールバックを実装することをお勧めします:最初にメディアのダウンロードを試み、成功したら完全なコンテンツでcontentHandlerを呼び出す;失敗したらテキストのみでメディアなしでcontentHandlerを呼び出す;重大なエラーの場合は元のコンテンツを渡します。このアプローチにより、ユーザーは空白の画面ではなく常に通知を表示できます。
Appleによると、タイムアウトの最も一般的な原因は、低速接続での大容量メディアファイルのダウンロードです。リスクを減らすには、サーバー上の画像サイズを最適化することをお勧めします — フル解像度ではなく、300KBまでのプレビューを送信します。フルサイズの画像はアプリを開いたときにダウンロードする必要があります。
拡張機能のタイムアウトやエラーを追跡するには、os_logを使用して統合ログシステムに診断メッセージを記録できます。拡張機能での直接ファイルログは困難ですが、os_logを使用すると開発者デバイスのConsole.appでパフォーマンス分析が可能です。Appleは各didReceive呼び出しにメトリクス(ダウンロード時間、ファイルサイズ、操作結果)を追加することを推奨しています。
override func serviceExtensionTimeWillExpire() {
let fallback = bestEffortContent as?
UNMutableNotificationContent
?? request.content.mutableCopy()
as! UNMutableNotificationContent
contentHandler(fallback)
}
よくある質問
サーバーはプッシュ通知のapsディクショナリにmutable-content:1キーを追加します。このパラメーターがないと、システムは拡張機能を無視して標準の通知を表示します。
いいえ。mutable-content:1はService Extensionを有効化するための必須条件です。キーがないか0に設定されている場合、拡張機能を呼び出さずに通知が表示されます。
iOSは強制的に拡張機能を終了し、変更されていない元の通知を表示します。これを防ぐには、最小限許容可能なコンテンツでserviceExtensionTimeWillExpire()を実装してください。
App Group(共有UserDefaultsまたはファイル)またはアプリと拡張機能間で共有アクセス可能なKeychainを介して行います。通知ペイロードで直接キーを渡すのは安全ではありません。
1つの通知につき最大4つの添付ファイル、各最大50MBです。添付ファイルの合計サイズはダウンロード時間に影響します — ファイルが多いほど、タイムアウトのリスクが高まります。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。