iOSではPush通知に画像を添付するために一手間かけないといけません。
それがNotification Service Extensionの導入です。
Firebaseを使いましたが、もちろんFirebaseを使わないPush通知にも応用可能なので、是非参考にしていただければと思います。
#導入
###Xcodeでの準備
- XcodeでProjectを開く
- メニューバーのFile->New->Targetを選択
###Developerサイトでの準備
Notification Service Extensionを利用するにはこのターゲット用のApplication IDとProvisioning Profileが必要になります。
アプリ用のものとは別のものを用意しないといけません。
-
DeveloperサイトにてCertificates, Identifiers & ProfilesでApplication Identifierを追加
※ここで大事な点があります
例えばアプリ用に用意しているBundle Identifierが
com.sample.application
だったとするならば
NotificationExtension用のBundle Identifierは
com.sample.application.extension
のように上位部分を合わせてください -
Provisioning Profileを作成
アプリ用のものと全く同じような作成方法で問題ありません
###Firebase側で気を付ける点
curl -X POST --header "Authorization: key=*******************" \
--Header "Content-Type: application/json" \
https://fcm.googleapis.com/fcm/send \
-d @- << EOF
{
"registration_ids": [
"********************************"
],
"notification": {
"title": "タイトル",
"imageUrl": "https://sample.*******.jpg"
},
"priority":10,
"mutable_content":true
}
EOF
送信時のコマンドはこのような感じになります
注意点としては
"mutable_content":true
こちらの書き方です
Firebaseのサポートサイトでは
mutable-content:1のような記述がありますが、上記の方法で送信することによってiOSの通知に変換される時にはmutable-content:1がちゃんと入っています。
これが入っていないと全く動きません。
###コーディング
自動で生成されたNotificationService.swiftを編集します。
var contentHandler: ((UNNotificationContent) -> Void)?
var bestAttemptContent: UNMutableNotificationContent?
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
if let urlString = request.content.userInfo["gcm.notification.imageUrl"] as? String,
let fileURL = URL(string: urlString) {
URLSession.shared.downloadTask(with: fileURL) { (location, response, error) in
if let location = location {
// メディアファイルをダウンロードしてtmpに保存
let tmpFile = "file://".appending(NSTemporaryDirectory()).appending(fileURL.lastPathComponent)
let tmpUrl = URL(string: tmpFile)!
try? FileManager.default.moveItem(at: location, to: tmpUrl)
if let attachment = try? UNNotificationAttachment(identifier: "hoge", url: tmpUrl, options: nil) {
// メディアを添付
self.bestAttemptContent?.attachments = [attachment]
}
}
contentHandler(self.bestAttemptContent!)
}.resume()
}
}
上記を用意しておけばOKです。
処理の流れとしては
Push通知が届くとdidReceiveが走り、メッセージの中にある画像URLをダウンロードして通知内容に添付します。
Extensionという名のように、通知の内容を拡張して書き換える、といったイメージです。
画像URLのリンクが切れていたりダウンロードに失敗してもcontentHandlerを実行するまでの時間制限が存在するのでPush通知自体が全く発生しなくなるといった心配はありません。
#嵌りポイント
私が何度やってもどうしてもうまくいかなかった嵌りポイントだけ紹介させて下さい。
それが、Extension用Targetの対応OSバージョンをアプリターゲットのOSと合わせないといけない
ということでした。
最初からextensionを用意していればこんなことにはならなかったのですが、アプリ用ターゲットの作成からextension作成までの時期に半年程の差があったので作成時の初期設定されているバージョンがずれてしまっていたというのが原因でした。
###終わりです
読んでいただきありがとうございました。
Brewus,Inc.
株式会社ブリューアス
https://brewus.co.jp