Firebase Cloud Messagingで画像つきプッシュ通知を送信する

  • 22
    いいね
  • 11
    コメント

はじめに

この間行われたtry! SwiftでFirebaseさんがいらしてたので頑張っていろいろ話していたらなんと
mutable-content に対応したよ」と言われたのでちょっとやってみました!

ちなみに前回「iOSアプリからFirebase Cloud Messaging経由でプッシュ通知を送る」という
記事を書いたのでその時に書いたプログラムを再利用しています。

環境

  • macOS Sierra
  • Xcode 8.2.1
  • Swift3.0.2
  • iOS10.x

サンプルソース

プッシュ通知設定関連

AppDelegate.swift
import UIKit
import UserNotifications

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        if #available(iOS 10.0, *) {
            UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound], completionHandler: { (grand, error) in
                guard error == nil else {
                    return
                }
                if grand {
                    UIApplication.shared.registerForRemoteNotifications()
                }
            })
        } else {
            let settings = UIUserNotificationSettings(types: [.alert, .badge, .sound],
                                                      categories: nil)
            application.registerUserNotificationSettings(settings)
            application.registerForRemoteNotifications()
        }
    }

    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        #if DEBUG
        FIRInstanceID.instanceID().setAPNSToken(deviceToken, type: .sandbox)
        #else
        FIRInstanceID.instanceID().setAPNSToken(deviceToken, type: .prod)
        #endif
    }
}
・・・

// MARK: - UNUserNotificationCenterDelegate

extension AppDelegate : UNUserNotificationCenterDelegate {
    @available(iOS 10.0, *)
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                willPresent notification: UNNotification,
                                withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        completionHandler([.alert, .badge, .sound])
    }

    @available(iOS 10.0, *)
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                didReceive response: UNNotificationResponse,
                                withCompletionHandler completionHandler: @escaping () -> Void) {
        completionHandler()
    }
}

とりあえずこんなふうなソースコードを書いておく。

プッシュ通知受け取ったときの動作(iOS10以上)

File 」→「 New 」→「 Target... 」で「 Notification Service Extension 」を追加する。

今回はFirebaseのpayloadに image_url を付けてプッシュ通知を送信してみる。
今回のpayloadサンプルはここで必要なソースコードの下に示します。

Notification/NotificationService.swift
import UserNotifications

class NotificationService: UNNotificationServiceExtension {

    let imageKey = AnyHashable("gcm.notification.image_url")

    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 imageUrl = request.content.userInfo[imageKey] as? String {
            let session = URLSession(configuration: URLSessionConfiguration.default)
            let task = session.dataTask(with: URL(string: imageUrl)!, completionHandler: { [weak self] (data, response, error) in
                if let data = data {
                    do {
                        let writePath = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("push.png")
                        try data.write(to: writePath)
                        guard let wself = self else {
                            return
                        }
                        if let bestAttemptContent = wself.bestAttemptContent {
                            let attachment = try UNNotificationAttachment(identifier: "nnsnodnb_demo", url: writePath, options: nil)
                            bestAttemptContent.attachments = [attachment]
                            contentHandler(bestAttemptContent)
                        }
                    } catch let error as NSError {
                        print(error.localizedDescription)

                        guard let wself = self else {
                            return
                        }
                        if let bestAttemptContent = wself.bestAttemptContent {
                            contentHandler(bestAttemptContent)
                        }
                    }
                } else if let error = error {
                    print(error.localizedDescription)
                }
            })
            task.resume()
        } else {
            if let bestAttemptContent = bestAttemptContent {
                contentHandler(bestAttemptContent)
            }
        }
    }

    override func serviceExtensionTimeWillExpire() {
        if let contentHandler = contentHandler, let bestAttemptContent =  bestAttemptContent {
            contentHandler(bestAttemptContent)
        }
    }
}

今回のpayload

Firebase Cloud Messagingのエンドポイントに向けて送るpayloadサンプルプログラムです。

プログラムの説明は前回の記事に書いてあるので参照ください。

payload.py
import daemon
import json
import pyrebase
import requests

config = {
    # GoogleService-info.plistのAPI_KEY
    "apiKey": "API_KEY",
    "authDomain": "PROJECT_NAME",
    "databaseURL": "https://PROJECT_NAME.firebaseio.com/",
    # いらないかもしれない
    "storageBucket": "gs://PROJECT_NAME.appspot.com"
}

fcm_header = {
    'Content-type': 'application/json; charset=UTF-8',
    # このコードの下の画像を参照してください!
    'Authorization': 'key=API_KEY'
}


firebase = pyrebase.initialize_app(config)


def stream_handler(message):
    if message['data'] == None:
        return
    if message['path'] == '/':
        return

    if 'title' in message['data']:
        title = message['data']['title']
    else:
        title = '配信'

    if 'body' in message['data']:
        body = message['data']['body']
    else:
        body = ''

    payload = {'to': '/topics/news', 
               'priority': 'high', 
               'notification': {
                   'title': title,
                   'body': body,
                   'badge': 1,
                   'sound': 'default',
                   'mutable_content': True,
               }
              }
    if 'image_url' in message['data']:
        payload['notification']['image_url'] = message['data']['image_url']

    r = requests.post('https://fcm.googleapis.com/fcm/send', \
                      headers=fcm_header, \
                      data = json.dumps(payload))
    print(r.json())


if __name__ == '__main__':
    db = firebase.database()
    dc = daemon.DaemonContext()
    with dc:
        stream = db.child("message").stream(stream_handler)

実行結果

Firebase Databaseにデータを追加

スクリーンショット_2017-03-14_21_36_30.png

前回と同じくこんな感じにFirebase Databaseにデータ追加します。

プッシュ通知結果

C6aiGhnVMAAvZVl.jpg

C6aiGffUwAAONdg.jpg

やったね!

最後に

今回はPythonのプログラムからまた送信してみました。
また、一応FirebaseConsole(Web)からもテストしてみたのですが、できなさそうなのでやはりこっちで対応する形になりそうです。

それとおまけで、Firebaseの中の人から
「近いうちに1to1プッシュ通知に対応するよ〜」
との情報もいただき、Firebaseのソース(タバスコ)などの大切なものもいただきました!