質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.50%

  • Swift

    7244questions

    Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

  • Xcode

    4098questions

    Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

  • iOS

    3998questions

    iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

  • Firebase

    606questions

    Firebaseは、Googleが提供するBasSサービスの一つ。リアルタイム通知可能、並びにアクセス制御ができるオブジェクトデータベース機能を備えます。さらに認証機能、アプリケーションのログ解析機能などの利用も可能です。

firebaseでリッチプッシュを送りたい

受付中

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 390

falconHM

score 2

Firebaseを使ってリッチプッシュを送りたいと思っています。
テストプロジェクトでPHPからPUSH送信して受信までは出来ているのですが、
リッチプッシュの確認が出来ません。

下記を参考にNotificationService.swiftを作成したのですがうまくいきません。。。
Firebase Cloud Messagingで画像つきプッシュ通知を送信する

下記ソースですが、これでPUSH送信しても通常のPUSHが受信される状態です。
NotificationService.swiftとAppDelegate.swiftの挙動に関してそこまで詳しくないので、
何か足りないものがあるのかと思うのですが。。。
それともPHP側で送る内容に不備があるのか不明な状況です。。。
※PHPファイルに「mutable_content」と「image_url」が多いのは色々試していたためですm(_ _)m

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 bestAttemptContent = bestAttemptContent {
            // Modify the notification content here...
            bestAttemptContent.title = "\(bestAttemptContent.title) [modified]"

            contentHandler(bestAttemptContent)
        }
        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() {
modified content, otherwise the original push payload will be used.
        if let contentHandler = contentHandler, let bestAttemptContent =  bestAttemptContent {
            contentHandler(bestAttemptContent)
        }
    }
}


PHP側の処理

$api_key = 'APIKEY';
$base_url = "https://fcm.googleapis.com/fcm/send";
$title = 'test';
$body = 'body';
$token_key = 'TOKENKEY';
$data = array(
    "registration_ids" => $token_key
    ,"mutable_content" => True
    ,"priority" => "high"
    ,"notification"    => array( 
        "title"    => $title
        ,"body"    => $body
        ,"badge" => 1
        ,"mutable_content" => True
        ,"image_url" => "画像URL"
    )
    ,"data"    => array( 
        "title"    => $title
        ,"body"    => $body
        ,"badge" => 1
        ,"image_url" => "画像URL"
        ,"mutable_content" => True
    )
);
$header = array(
    "Content-Type:application/json"
    ,"Authorization:key=".$api_key
);
$context = stream_context_create(array(
    'http' => array(
        'method' => 'POST'
        ,'header' => implode("\r\n",$header)
        ,'content'=> json_encode($data)
    )
));
file_get_contents($base_url,false,$context);

AppDelegate.swift

import UIKit
import UserNotifications
import Firebase

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    let gcmMessageIDKey = "gcm.message_id"

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

        FirebaseApp.configure()

        Messaging.messaging().delegate = self
        if #available(iOS 10.0, *) {
            UNUserNotificationCenter.current().delegate = self

            let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
            UNUserNotificationCenter.current().requestAuthorization(
                options: authOptions,
                completionHandler: {_, _ in })
            UIApplication.shared.registerForRemoteNotifications()
        } else {
            let settings: UIUserNotificationSettings =
                UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
            application.registerUserNotificationSettings(settings)
        }

        application.registerForRemoteNotifications()

        return true
    }
    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
        if let messageID = userInfo[gcmMessageIDKey] {
            print("Message ID: \(messageID)")
        }
    }
    func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
        print("Unable to register for remote notifications: \(error.localizedDescription)")
    }

    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
                     fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
        if let messageID = userInfo[gcmMessageIDKey] {
            print("Message ID: \(messageID)")
        }

        completionHandler(UIBackgroundFetchResult.newData)
    }

    // ~~~~

    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        print("APNs token retrieved: \(deviceToken)")
        let token = deviceToken.map { String(format: "%.2hhx", $0) }.joined()
        print("deviceToken!! = \(token)")
    }

}

// [START ios_10_message_handling]
@available(iOS 10, *)
extension AppDelegate : UNUserNotificationCenterDelegate {

    // Receive displayed notifications for iOS 10 devices.
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                willPresent notification: UNNotification,
                                withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        let userInfo = notification.request.content.userInfo

        // With swizzling disabled you must let Messaging know about the message, for Analytics
        // Messaging.messaging().appDidReceiveMessage(userInfo)
        // Print message ID.
        if let messageID = userInfo[gcmMessageIDKey] {
            print("Message ID: \(messageID)")
        }

        // Print full message.
        print(userInfo)

        // Change this to your preferred presentation option
        completionHandler([])
    }

    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                didReceive response: UNNotificationResponse,
                                withCompletionHandler completionHandler: @escaping () -> Void) {
        let userInfo = response.notification.request.content.userInfo
        // Print message ID.
        if let messageID = userInfo[gcmMessageIDKey] {
            print("Message ID: \(messageID)")
        }

        // Print full message.
        print(userInfo)

        completionHandler()
    }
}
// [END ios_10_message_handling]

extension AppDelegate : MessagingDelegate {
    // [START refresh_token]
    func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
        print("Firebase registration token: \(fcmToken)")

        // TODO: If necessary send token to application server.
        // Note: This callback is fired at each app startup and whenever a new token is generated.
    }
    // [END refresh_token]
    // [START ios_10_data_message]
    // Receive data messages on iOS 10+ directly from FCM (bypassing APNs) when the app is in the foreground.
    // To enable direct data messages, you can set Messaging.messaging().shouldEstablishDirectChannel to true.
    func messaging(_ messaging: Messaging, didReceive remoteMessage: MessagingRemoteMessage) {
        print("Received data message: \(remoteMessage.appData)")
    }
    // [END ios_10_data_message]
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

+1

まず、NotificationServiceExtentionが呼ばれているかを確認してください。
 
呼ばれている場合は、何も手を加えていない初期状態のNotificationServiceで、
(bestAttemptContent.title) →(bestAttemptContent.title)  [modified]
と、プッシュ通知のタイトル末尾に[modified]が表示されているはずです。

プッシュ通知自体が受信できているとのことなので、[modified]が表示されていないのであれば、プロビジョニングの設定ができていないのではないかと思います。App本体と別に、プロビジョニングプロファイルの設定を行う必要があります。

その場合、Developerサイトにて以下の作業をしてください。

  1. App ID取得
    ※App ServicesでPush Notificationsをオンにしなくていいです
    ※Bundle IDはアプリ本体の下の階層になるように設定してください
    (例)[本体]com.hogehoge.Fuga  -> [今回]com.hogehoge.Fuga.NotificationSerivce
  2. そのApp IDでProvisioning Profilesを取得
  3. ダウンロードしたファイルをダブルクリックで登録
  4. XcodeでNotificationSerivceのターゲットを選んで、プロビジョニングファイルを設定する

これでNotificationServiceExtentionが呼ばれるようになったら、「mutable_content」と「image_url」の話に移ります。

実際にPHPで試してないのでわかりませんが、
payload > data 内に

image”=> ”https://sample.com/image/sample.png”,


payload > notification内に 

"mutable_content"=>true,


と記載すればいけるかと思います。

AppDelegateのdidReceiveRemoteNotification内で、print(userInfo)をしたときに、Xcodeのログで

[AnyHashable("image"): https://sample.com/image/sample.png, 
 AnyHashable("aps"): {
    alert =     {
        body = “bodyが入る”;
        title = “titleが入る”;
    };
    "mutable-content" = 1;
}]]


という感じで表示されていたら成功です(今回関係ない項目は割愛しています)。

NotificationServiceのコードについては、質問に書いていただいた参考リンクの中で、

if let imageUrl = request.content.userInfo[imageKey] as? String


としている箇所があると思いますが、今回私が回答した構成ですと、

if let imageUrl = request.content.userInfo[“image”] as? String


にする必要があります。あとはリンク先のコードままでいけるように思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/06/23 11:59

    大変ご丁寧に回答頂きありがとうございます!
    プロビジョニングの設定必要なんですね!!
    (Developerサイトの説明まで!泣)
    これは知りませんでした。。。
    PUSH通知の確認方法までご丁寧にありがとうございます!
    早速確認してみようと思います!

    キャンセル

  • 2018/06/25 19:58

    確認して見ました!
    まず、NotificationServiceを通っていることの確認が出来ました!
    ですが、どうやら下記の部分でこけてしまっているようです。
    let task = session.dataTask(with: URL(string: imageUrl)!, completionHandler: { [weak self] (data, response, error) in

    imageUrlにもきちんとした値が入っているのですが。。。
    何か良い確認方法はないでしょうか?

    キャンセル

  • 2018/06/26 09:09

    URLは受け取れたとのことで、あと少しですね!
    やはり元のコードを見る限り、useInfoのソースを変えるだけで通るように見えます。

    ちょっとこれだけでは分からないので、XcodeのログやdataTask関数でエラーをprintしている箇所などを見て対処してください。
    アップロード完了前にPUSHが届いたりはしてないですよね?

    キャンセル

  • 2018/07/03 11:00

    ご連絡遅くなりました!ちょっと別件作業がとても忙しくてみれていない状況です。。。「アップロード完了前にPUSHが届いたりはしてないですよね?」のアップロード完了とは端末に画像をアップロードする。という認識で合っていますでしょうか?それならばアップロードの処理に到達(if文を通っていない)していない状況になります!
    来週には時間作れると思うのでログを出しながらじっくり追ってみます!

    キャンセル

  • 2018/07/03 19:17 編集

    少し時間が出来たので確認してみました!
    ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
    let task = session.dataTask(with: URL(string: imageUrl)!, completionHandler: { [weak self] (data, response, error) in
    ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
    こちらが通らない状況です。
    imageUrlにはきちんとセットした画像のURLが届いていますし、
    試しにimageUrlをNotificationService.swift内でURL上書きしても通らない状況です。
    ViewController.swiftであれば問題なく通るんですが、もしかして何かXcodeで設定しないとNotificationService.swiftではHTTP通信出来ない等ありますでしょうか???

    キャンセル

  • 2018/07/03 19:43

    NotificationService.swift内のprint()ログを表示させる方法ってありますでしょうか。。。?

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.50%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • Swift

    7244questions

    Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

  • Xcode

    4098questions

    Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

  • iOS

    3998questions

    iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

  • Firebase

    606questions

    Firebaseは、Googleが提供するBasSサービスの一つ。リアルタイム通知可能、並びにアクセス制御ができるオブジェクトデータベース機能を備えます。さらに認証機能、アプリケーションのログ解析機能などの利用も可能です。