FlutterでFirebase Cloud MessagingとNotification Service Extension

先で書いた [FlutterでFirebase Cloud Messaging](https://qiita.com/yoshua/items/3c9d6674d30788b5d002) を基に、iOSのNotification Service Extensionを使った拡張を追加する。

これで以下を実現する。

- 通知センターに表示する通知に画像を表示する。

本ページより参照しているApple, Firebaseなどのページは以下。

- https://developer.apple.com/documentation/usernotifications/modifying_content_in_newly_delivered_notifications
- https://firebase.google.com/docs/cloud-messaging/ios/send-image

# 証明書などの用意

先で書いた [FlutterでFirebase Cloud Messaging](https://qiita.com/yoshua/items/3c9d6674d30788b5d002) では以下の証明書を用意した。

- iOS App Development
- Apple Push Notification service SSL (Sandbox)

前者のiOS App Developmentは特定のBundle identifierの証明書。
今回追加するNotification Service Extensionでも証明書が必要なので、Apple Developmentの証明書を用意した。

最終的には以下のようなApp ID, 証明書, Provisioning Profileを用意した。

- App ID
    - アプリ用のApp ID
    - Notification Service Extension用のApp ID
- macOSにインストールした証明書
    - Apple Developmentの証明書
    - Apple Push Notification service SSL (Sandbox)の証明書
- macOSにインストールしたProvisioning Profile
    - アプリ用のApp IDのProvisioning Profile (証明書はApple Development)
    - Notification Service Extension用のApp IDのProvisioning Profile (証明書はApple Development)


アプリ用のApp IDのProvisioning Profile
![スクリーンショット 2020-06-21 21.44.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/375661/f85a2962-361a-55ac-a36b-97501b4a0d21.png)

Notification Service Extension用のApp IDのProvisioning Profile
![スクリーンショット 2020-06-21 21.52.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/375661/be1d3680-0b75-cfb7-5b94-499179455766.png)

# Notification Service Extension

以下はXcode 11.5で確認。

手順は https://developer.apple.com/documentation/usernotifications/modifying_content_in_newly_delivered_notifications に記述されているので、最新のXcodeを使用している場合はドキュメントを見直したほうが良い。


## Notification Service Extensionの設定など

File > New > Target の順に選び Notification Service Extension を選択する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/375661/f502851b-be07-0cd2-03f9-98cd6e5a951b.png)

Product Name、Languageを入力し進む。
LanguageのデフォルトはSwiftだがFirebaseのExtensionがSwiftで使えなかったこと（またドキュメントでもSwiftのコードの説明もなかったこと）から、Objective-Cを選択している。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/375661/71230362-81e7-928e-e021-ebb28bc94ea3.png)


次に進むとActivateするかどうか確認される。Activateしなかった場合がわからないが、ひとまずActivateをする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/375661/9ce305b6-f8b3-b0d0-10d5-e406f74340a5.png)

追加した Notification Service Extension の Build Settings を開き、 Enable Bitcode を No にする。（FlutterがBitcodeに対応していないので）
![スクリーンショット 2020-06-21 22.05.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/375661/d7f44b75-2366-a5ee-2f0d-f6e7e6cacb3b.png)

## ひとまずここまでで通知が届くか確認

まだ最終ゴールまでたどりついていないが、ここでいったん通知が届くことを確認をしておく。
Firebase Consoleから通知を送り届くかどうか確認をする。タイトルに `[modified]` と追加された通知を受信できる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/375661/efc55d47-01bd-5b9a-08f7-5c1b8573cc7c.png)

届かなくなってしまったのであれば、特に証明書周りを疑ったほうが良い。

タイトルに `[modified]` と追加されているのは、先の手順で追加した Notification Service Extension のデフォルトの実装でそうなっているので。

## 画像が表示されるようにする変更

画像が表示されるようにする変更は https://firebase.google.com/docs/cloud-messaging/ios/send-image のコードをそのまま使えば良い。
FirebaseMessagingが提供するAPIを使えばすぐできる。

```objective_c

#import "NotificationService.h"
#import <FirebaseMessaging/FirebaseMessaging.h>

@interface NotificationService ()

@property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver);
@property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent;

@end

@implementation NotificationService

- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
    self.contentHandler = contentHandler;
    self.bestAttemptContent = [request.content mutableCopy];
    
    // Modify the notification content here...
    [[FIRMessaging extensionHelper] populateNotificationContent:self.bestAttemptContent withContentHandler:contentHandler];
}

- (void)serviceExtensionTimeWillExpire {
    // Called just before the extension will be terminated by the system.
    // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
    self.contentHandler(self.bestAttemptContent);
}

@end
```

## 試す

Doodleのanimation gifで試してみた。
https://www.google.com/doodles/swedish-midsummer-2020

結果はこんな感じ。うまく動いた。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/375661/b6cd5807-6101-ab16-9eba-a4d6fbf3face.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/375661/abfa77fa-a9f6-b508-a090-fac96ccb0797.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/375661/3dbe11cf-8845-5b0e-a77a-8872c7332cbb.png)

通知センターでanimation gifがアニメーションしてていい感じ。