LoginSignup
2
3

More than 3 years have passed since last update.

追加要件:FCMにて画像付きPush通知を送る iOS10以上

Posted at

前置き

  • FCMコンソールからのPush通知が実装済みからの追加要件という内容です。
  • 今回の記事では送信方法としてPostmanを使用しております。
  • なるべく細かく書くつもりですが、後半になるにつれ適当になる可能性があります。

概要

iOS10から追加された Notification Service Extension を利用する。
これは「送信されたペイロード」 -> 「端末に表示」 までの間の部分をあれこれ出来る。
そのため表示される前に画像を読み込んで、それを表示させるといった流れを行える。

目次

  • Notification Service Extension追加
  • Notification Service Extension用のProvisioningファイルを用意
  • Notification Service Extension編集
  • 送信

Notification Service Extension追加

  1. XcodeメニューバーのFile -> New -> Target
  2. Notification Service Extension を選択 -> Next
    スクリーンショット 2020-06-03 11.19.05.png

  3. Product Nameを入力(ex: NotificationService) -> Finish

    「MainProjectのBundleIdentifier.上記ProductName」という形式のBundleIdentifierとなる。
    言わずともこれは後述するProvisioningファイルのBundle IDとなる。

  4. ダイアログが出たらActivateを選択

  5. NotificationServiceExtensionディレクトリ配下にNotificationService.swiftInfo.plistがあるものが追加されていればOK
    NotificationService.swiftには下記2つのメソッドが既存で作成されている。

NotificationService
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
    ///省略
}
override func serviceExtensionTimeWillExpire() {
    ///省略
}

Notification Service Extension用のProvisioningファイルを用意

MainAppと異なるBundleIdentifierをもつTargetが追加されたため、
これに対してProvisioningファイルを用意する必要がある。

  1. AppleDeveloper SignIn
  2. Certificates, Identifiers & Profiles -> Identifiers -> 「+」押下
    スクリーンショット 2020-06-03 10.59.43.png

  3. App IDs 選択 -> Continue

  4. 各項目入力 -> 作成

    Description: NotificationServiceExtension
    Bundle ID: 「MainProjectのBundleIdentifier.先程のProductName」
    ※コピーするとエラーが出ることがあるようで、その場合は手動入力をする必要がある。

  5. Certificates, Identifiers & Profiles -> Profiles -> 「+」押下

  6. App Store -> 先ほど作成したApp IDs選択 -> メインと同じCertificateファイルで作成

  7. 作成すると既存Provisioningファイルが無効になるため Edit -> Save をし更新する。

  8. xcodeに戻る。

  9. XcodeメニューバーのXcode -> Preferences -> Download Manual Profiles より先ほど作成したProvisioningファイルをDL

  10. 各TARGETSに適切なProvisioningProfileを選択
    スクリーンショット 2020-06-03 12.11.27.png

  11. MainAppのSigning & CapabilitiesにBackground Modesを追加
    スクリーンショット 2020-06-03 12.13.42.png

Notification Service Extension編集

NotificationService.swift
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {

    self.contentHandler = contentHandler
    bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)

    self.realmWrite(request: request, bestAttemptContent: bestAttemptContent)

    if let imageUrl = request.content.userInfo["image_url"] as? String {
        let session = URLSession(configuration: URLSessionConfiguration.default)
        let task = session.dataTask(with: URL(string: imageUrl)!, completionHandler: {[weak self](data, response, error) in
            do {
                if let writePath = NSURL(fileURLWithPath:NSTemporaryDirectory())
                    .appendingPathComponent("tmp.jpg") {
                    try data?.write(to: writePath)
                    let identifier = "hogehoge"

                    if let bestAttemptContent = self?.bestAttemptContent {
                        let attachment = try UNNotificationAttachment(identifier: identifier, url: writePath, options: nil)
                        bestAttemptContent.attachments = [attachment]
                        contentHandler(bestAttemptContent)
                    }
                } else {
                    if let bestAttemptContent = self?.bestAttemptContent {
                        contentHandler(bestAttemptContent)
                    }
                }
            } catch let error as NSError {
                print(error.localizedDescription)

                if let bestAttemptContent = self?.bestAttemptContent {
                    contentHandler(bestAttemptContent)
                }
            }
        })
        task.resume()
    } else {
        if let bestAttemptContent = self.bestAttemptContent {
            contentHandler(bestAttemptContent)
        }
    }
}

override func serviceExtensionTimeWillExpire() {
    if let contentHandler = contentHandler, let bestAttemptContent =  bestAttemptContent {
        // 実行時間をすぎた場合の救済
        contentHandler(bestAttemptContent)
    }
}

送信

送り先

POST: https://fcm.googleapis.com/fcm/send

Header

Header
Content-Type:application/json
Authorization:key=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx  

スクリーンショット 2020-06-03 12.24.22.png

  1. Firebase
  2. 歯車アイコン -> プロジェクト設定 -> CloudMessaging
    スクリーンショット 2020-06-03 12.27.11.png

  3. サーバーキーのトークンをコピー
    スクリーンショット 2020-06-03 12.31.17.png

  4. 「xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx」をコピーしたトークンに変更

Body

Body
{
    "data" : {
        "image_url":"https://hogehoge.jp/fugafuga.png",
        "type" : "1"
    },
    "notification" : {
        "title": "タイトル",
        "body": "ボディ"
    },
    "mutable_content": true,
    "content_available": true,
    "to" : "token or topic"
}

スクリーンショット 2020-06-03 12.24.55.png

Key:toに指定するもの

  1. 少しAppDelegateに追加
AppDelegate.swift
extension AppDelegate: MessagingDelegate {
    func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
        print("fcmToken:\(fcmToken)")
        Messaging.messaging().subscribe(toTopic: "/topics/global") { error in
            if let error = error {
                print("TopicErrorRegistError: \(error)")
            }
        }
    }
}

この状態でビルドし、コンソールに表示されたものを利用。

  • デバイス単体に送りたい場合はfcmToken:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx部分
  • topic購読している全デバイスに送りたい場合は/topics/global

toTopicはお好きなもので。

いざ!送信!!
届かなかった方はコメントください。

追記

Debug

NotificationServiceがきちんと走っている??
と気になりDebugで止めたい場合はMainAppをビルドし一旦メインをデバイスに流した後、NotificationServiceを指定してビルドすることで可能

スクリーンショット 2020-06-03 12.52.35.png

脱線

今回の内容と少し違いますが
AppGroupsを利用することでMainとPushに共通データを持つことが可能。
例えば通知を見逃したユーザのために、通知履歴などをアプリに実装。

~流れ~
1. 通知を送る
2. NotificationService.swiftにてRealm等に保存処理(SharingPath)
3. MainAppにてSharingPathを元に通知Appにて保存したデータを共有Realmから読み取る
4. データを元に履歴List画面作成

jsonのdataに任意の値(この記事でいうtype)を追加するなどすれば、履歴リストからタップし、画面遷移なども行えますね!

需要がありそうなら細かく書きますが、、
こんなこともできますよーという話でした。

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3