[iOS] Push通知に画像や動画などのファイルを添付する

本記事ではPush通知に画像や動画などのファイルを添付する実装を紹介します。
2020.05.11

はじめに

こんにちは。CX事業本部の平屋です。

本記事ではPush通知に画像や動画などのファイルを添付する実装を紹介します。

iOS 10 で追加された User Notifications framework の機能を使用して通知へのファイル添付を実現します。

検証環境

  • macOS Mojave 10.15.3
  • Xcode Version 11.3.1
  • iPhone X, iOS 13.3

対応ファイル形式

画像、映像、音声 を添付できます。具体的な形式はSupported File Typesに記載されています。

添付ファイルはアプリ側でダウンロードする必要があります。アプリ側での処理に使えるのは 最大30秒 なのでファイルサイズはなるべく小さい方が良いです。

実装内容

Push通知に画像や動画などのファイルを添付するには、主に以下の処理をアプリに実装します。

  • プッシュ通知のペイロードから添付ファイルのURLを取り出す
  • 添付ファイルをダウンロードし、OSに提供する

またアプリの実装以外に関しては以下の対応が必要です。

  • 添付ファイルをアプリから参照できる任意の場所に置く
  • プッシュ通知のペイロードに添付ファイルのURL情報を含める

アプリの実装

通常のPush通知と同様の処理

通常のPush通知と同様に、以下の処理などを実装します。

  • CapabilitiesのPush NotificationsをONにする
  • 通知の使用許可のリクエスト
  • デバイストークンの取得
  • 通知受信時の処理

上記処理の実装方法は以下の記事で解説しています。

Notification Service app extension

Extensionの追加

Notification Service app extension というExtensionをアプリに追加します。

Extensionの概要や追加方法は以下の記事で解説しています。

Extensionの実装

Extensionを追加できたら、ExtensionのNotificationServiceクラス内に以下の処理を実装します。

  • (1) プッシュ通知から添付ファイルのURLを取り出す
  • (2) 添付ファイルをダウンロードする
  • (3) 添付ファイルの保存先のURLをOSに提供する

例えば以下のように実装します。

class NotificationService: UNNotificationServiceExtension {
    // 「リモート通知が端末に届いた後」かつ「ユーザーに通知を見せる前」のタイミングで呼ばれる
    override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
        guard let content = (request.content.mutableCopy() as? UNMutableNotificationContent) else {
            contentHandler(request.content)
            return
        }

        // (1) プッシュ通知のペイロードから添付ファイルのURLを取り出す
        // ペイロードのルートの`url`キーに対応する値がURLだと想定する
        guard let urlString = request.content.userInfo["url"] as? String,
            let url = URL(string: urlString) else {
            // ペイロードからurlを取り出せない場合
            contentHandler(content)
            return
        }

        // (2) 添付ファイルをダウンロードする
        let task = URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in
            // URLの末尾が拡張子付きのファイル名であると想定する
            let fileName = url.lastPathComponent
            let writePath = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(fileName)

            do {
                try data?.write(to: writePath)

                // (3) 添付ファイルの保存先のURLをOSに提供する(contentHandler経由で)
                let attachment = try UNNotificationAttachment(identifier: fileName, url: writePath, options: nil)
                content.attachments = [attachment]
                contentHandler(content)
            } catch {
                print("error: \(error)")
                contentHandler(content)
            }
        })
        task.resume()
    }
}

アプリ以外の実装

ファイルを配置する

添付ファイルをアプリから参照できる任意の場所に配置します。

プッシュ通知を送信する

ペイロードに添付ファイルのURL情報を含めて送信します。

ペイロードの形式

ペイロードの例は以下の通りです。

{
    "aps": {
        "alert": {
            "title": "お知らせ",
            "body": "テスト"
        },
        "mutable-content": 1
    },
    "url": "https://example.com/001.jpg"
}

添付ファイルのURLはルートのディクショナリの中で{任意のキー} : {ファイルのURL}という形式で指定します。(上記の例の場合は"url": "https://esample.com/001.jpg")

また、Extensionの実装が呼ばれるようにするためにaps内に"mutable-content": 1を指定します。

プッシュ通知を送信する

  • プッシュ通知の送信にnode-apnを使用する場合、以下のような実装でファイルが添付されたプッシュ通知を送信できます。
var apn = require('apn');

// Set up apn with the APNs Auth Key
var apnProvider = new apn.Provider({
     token: {
        key: 'AuthKey_XXXXXXXXXX.p8',
        keyId: 'XXXXXXXXXX',
        teamId: 'YYYYYYYYYY',
    },
    production: false // Set to true if sending a notification to a production iOS app
});

// Enter the device token from the Xcode console
var deviceToken = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';

// Prepare a new notification
var notification = new apn.Notification();

// Specify your iOS app's Bundle ID (accessible within the project editor)
notification.topic = 'com.example.RemoteNotificationWithAttachmentsSample';

// Set expiration to 1 hour from now (in case device is offline)
notification.expiry = Math.floor(Date.now() / 1000) + 3600;

// Display the following message (the actual notification text, supports emoji)
notification.alert = {"title": "お知らせ", "body" : "テスト"};

notification.mutableContent = 1
notification.payload = {"url": "https://example.com/001.jpg" };

// Actually send the notification
apnProvider.send(notification, deviceToken).then(function(result) {
    // Check the result for any failed devices
    console.log(result);
});

動作確認

Push通知受信時のスクショは以下の通りです。

画像を添付した場合

ホーム画面を表示中の場合

Push通知が届くと画面上部に通知バナーが表示され、画像は右端に表示されます。(画像は正方形にトリミングされる)

バナーを長押しすると通知バナーが展開されて、添付ファイルが拡大表示されます。バナーをタップすると、アプリが起動します。

動画を添付した場合

ホーム画面を表示中の場合

Push通知が届くと画面上部に通知バナーが表示され、動画のサムネイル画像が右端に表示されます。(画像は正方形にトリミングされる)

バナーを長押しすると通知バナーが展開されて、動画のサムネイル画像が拡大表示されます。

画像をタップすると、動画の再生が始まります。画像の領域以外をタップすると、アプリが起動します。

音声を添付した場合

ホーム画面を表示中の場合

Push通知が届くと画面上部に通知バナーが表示されます。(サムネイル画像などは特に表示されない)

バナーを長押しすると音声再生用のコンポーネント(再生ボタンとシークバー)が表示されます。

再生ボタンをタップすると、音声の再生が始まります。(この再生ボタンは反応がとても悪く、高確率で音声を再生できなかった。iPhone XS, iOS 13.3.1で確認。)

音声再生用のコンポーネント領域以外をタップすると、アプリが起動します。

さいごに

本記事ではPush通知に画像や動画などのファイルを添付する実装を紹介しました。

通知へのファイル添付については、以前以下の記事で紹介しましたが、動画や音声を添付した場合の動作結果などがなかったので今回改めて記事を書きました。Push通知へのファイル添付の実装をやろうとしている方の参考になれば幸いです。

参考資料