🤧

ReactNative+FCMでiOSに画像をpush通知するときのつまづきポイント

2021/12/17に公開

この記事はスターフェスティバル Advent Calendar 2021の17日目です。 We're hiring!

--

こんにちは、スターフェスティバル エンジニアのsoriです。Advent Calendarの他の記事では個人ブログにて掲載していましたが、今回の記事は社Zennアカウントから投稿してみます。

今回は、React Native環境でFirebase Cloud Messaging(以下FCM)にて、画像付きのPush notificationを送りたい場合につまづいた点を、備忘録ついでにご紹介したいと思います。

なお、前提としてReact Native Firebaseライブラリの導入が完了しており、テキストのみの通知がすでにできている状態を想定しています。

基本の使い方について

基本的には、React Native Firebaseの公式ドキュメントのとおりに作業していくのが一番安全かと思います。

他にも解説記事やFirebase自体のドキュメントにも記載はありますが、XCodeバージョンやその他環境差異によりコードが違ったりするのであくまでも後位の優先順位にとどめておくのがおすすめです。

Notification拡張の変更箇所が記事によりまちまち

React Nativeを使っているということは、iOSのネイティブ環境(Objective-CやSwift等)の開発に慣れていないことがあると思います。(私もそうです)ですから、こちらも先程のドキュメントのとおりにしておけば大丈夫なのですが、記事により違う点の解説を軽くしたいと思います。

importの記述について

記事によっては、Notification拡張の導入をしたあと、直接[[FIRMessaging extensionHelper]で始まるコードの記述が書いてあるかもしれません。しかし、react-native-firebaseライブラリを使用している場合は下記の記述が必須です。

#import "FirebaseMessaging.h"

#import "RNFBMessaging.h" の記述があるものは古いドキュメントです。

削除箇所について

記事によっては、下記箇所の削除が省かれているものがあります。

self.bestAttemptContent.title = [NSString stringWithFormat:@"%@ [modified]", self.bestAttemptContent.title];

こちらのコメントアウトを省いても動作はしますが、通知内容のタイトルの先頭に[modified]がついてしまうようになります。分かる人にとってはコードを読めば明白ではあるのですが、最初よく読んでなくてつまづきました。

ポイントは下記箇所の書き換えです。

- self.contentHandler(self.bestAttemptContent);
+ [[FIRMessaging extensionHelper] populateNotificationContent:self.bestAttemptContent withContentHandler:contentHandler];

self.contentHandler()FIRMessaging extensionHelperで置き換える箇所が重要のようです。これにより、ネイティブの通知を書き換え、なおかつiOS環境においては画像のダウンロード及び通知領域への送信をライブラリが肩代わりしてくれることになります。

追加箇所について記事によっては改行が入りますが、ココの編集を別のエディタではなくXCodeのエディタですることにより、いい感じで改行を入れてくれるので便利です。

また、記事によっては生成されたImageNotification.m内の<URLSessionDelegate>や、(nonatomic, strong)strongがあったりなかったりしますが、そこは生成されたままで変えなくて大丈夫です。あくまで指定された箇所のみ変更しましょう。

Notification拡張のbundle identifierの修正について

基本的にアプリに対して一つのbundle identifierで記述をしていれば、ココで躓くことはないかと思いますが、私の環境ではapp storeへ本番リリースする環境と開発環境でbundle identifierを変更し、同時に開発環境と本番環境のアプリのインストールを可能にしています。
例えば下記のような形です。

// 本番環境
jp.co.stafes.exampleapp
// 開発環境
jp.co.stafes.exampleapp.dev

このように環境ごとに切り替えを行っている場合、Notification拡張のbundle identifierは下記のように自動生成されていることでしょう。

// (ImageNotificationは拡張作成時に決めた任意の名前に読み替えてください)
jp.co.stafes.exampleapp.ImageNotification

このようなbundle identifierで拡張を生成した場合、開発環境でビルドしようとすると、prefixが異なるエラーによりビルドエラーとなってしまいます。

その場合、拡張自体のbundle identifierも環境により切り替える必要があります。

XCodeのプロジェクトの全体設定にて、拡張のTargetを選択し、下記のように環境ごとにそれぞれメインのApplicationのbundle identifierのprefixに揃えた形のidentifierに設定しましょう。

// 本番環境
jp.co.stafes.exampleapp.ImageNotification
// 開発環境
jp.co.stafes.exampleapp.dev.ImageNotification

実機確認時にSigning Provisioningのエラーが出る

こちらの開発環境では、開発環境にビルドをする際にfastlaneを使ったビルドをしているのですが、上記設定をしても、実機へのビルドが成功しませんでした。

なかなか公式ドキュメントではわかりにくいのですが、拡張に対するApp IDの設定が必要ということがわかりました。

AppleのiOS developerサイトでアプリ自体のApp ID、Provisioning profileの作成をしたかも知れませんが、これはNotification拡張についても同じです。

上記例だと、jp.co.stafes.exampleapp.ImageNotificationおよびjp.co.stafes.exampleapp.dev.ImageNotificationについてそれぞれApp ID, Provisioning profileを生成し、実行して取り込んでおきましょう。fastlaneを使用している場合は、export設定のprovisioningProfileの配列に、bundle identifierとProvisioning profileの組み合わせの設定の追加が必要になるでしょう。

なお、この拡張自体のProvisioning profileについて、Push NotificationのCapabilitiesへの追加は必要ありません。

以前のバージョンからreact-native-firebaseを使っている場合

ドキュメントを見直したところ当初のpush notificationの設定時と比較して、ApplicationのCapabilitiesの権限について、Background ModesBackground Fetchの権限設定が追加されていました。ここの設定が躓いたことに影響しているかはわかりませんが、ドキュメントに載っているので合わせて設定しておきましょう。

結果、この通知に必要な最低限の権限は下記のとおりとなります。

  • Background Modes
    • Background fetch
    • Remote notifications
  • Push Notifications

Firebase公式ドキュメントとpayloadの指定が違う

ライブラリの方ではなく、Firebase自体の通知のpayload見本を見ると、下記のような違いがあります。基本、Typescriptの定義に合わせる形で大丈夫です。

// Firebase
apns.payload.aps.mutable-content: 1
// firebase-reactnative
apns.payload.aps.mutableContent: true

// Firebase
apns.fcm-options.image: <image url>
// firebase-reactnative
apns.fcmOptions.imageUrl: <image url>

他にもcontentAvailableanalyticsLabel、またプラットフォーム依存のオプションといった、有用なオプションがありますのでドキュメントは読み込んでおきましょう。
最終的に出来上がったのは下記のようなpayloadです。

const params: MulticastMessage = {
  tokens,
  notification: {
    title,
    body,
    imageUrl,
  },
  android: {
    notification: {
      imageUrl,
    },
  },
  apns: {
    payload: {
      aps: {
        mutableContent: true,
      },
    },
    fcmOptions: {
      imageUrl,
    },
  },
}
}

(12/21追記) Notification拡張のTarget Versionは確認しよう

上記解決したと思っていた矢先、開発バージョンの配布先により画像が表示されないという問題が発生しました。

しばらく悩んで調査したところ、メインのアプリケーションのTargetのOS Target Version12.0だったのに対し、Notification拡張のOS Target Version15.2(追加時の最新)になっていました。ですから、たまたまデバッグ機が最新でiOS15.2だったので確認できたのにも関わらず、配布先はiOS15.1だったので拡張のみ認識できなかったというわけです。

その後、両方のTarget Versionを12.0に揃えることで解決。困ったときはXCodeのGeneral Settingsなども見直してみることも大切ですね…

さいごに

ReactNativeでの開発は少し凝ったことをするとつまづくポイントがたくさんありますが、根気よくドキュメントの読み込み、トライアンドエラーで解決策は出てきますので頑張っていきましょう〜。

スタフェステックブログ

Discussion