Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
47
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

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

はじめに

この間行われた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のソース(タバスコ)などの大切なものもいただきました!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
47
Help us understand the problem. What are the problem?