【Xcode】Notification Content Extension で Notification のカスタマイズ (iOS12対応版)

下記ブログの転載です
https://rc-code.info/ios/post-61/


iOS12（したがって開発環境はXcode10.0）から **Notificationの通知内容上でインターフェースを自由に使うことができる** ようになりました。
Notificationの表示コンテンツをカスタマイズする Notification Content Extension 自体はiOS10から利用できましたが、改めて調べたので備忘録。


## Notificationのカスタマイズ iOS12
Notification Content Extension とはアプリ内にモジュールを用意しておくことで、アプリのNotification着信時にカスタマイズされたViewControllerを表示させる機能です。

<img class="size-medium" src="http://rc-code.info/wp-content/uploads/2018/ios-swift/notificationContentAppExtention/001.png" alt="サンプル画像1" width="884" height="386" />

<img class="size-medium" src="http://rc-code.info/wp-content/uploads/2018/ios-swift/notificationContentAppExtention/002.png" alt="公式サンプル画像2" width="919" height="454" />

### カスタマイズできる事項
- アラートのタイトル、サブタイトル、本文を含むアイテムの配置する
- インタフェース要素を異なるフォントやスタイリングに置き換える
- アプリケーション固有のデータを扱う（たとえば、通知のペイロードで受け取った固有キーから格納されているデータを引き出したり）
- カスタム画像やブランディング要素を含める
&nbsp;

Notification Content Extension を使えば `モジュール` に対して公開してされているアプリ内の様々なデータや機構を扱えます。
つまり、動的なデータや情報をアプリから取得し、表示要素として扱うことも可能になります。

ただし、相変わらずランタイムを圧迫するような処理（ネットワークからデータを受け取って表示するなど）は控えた方が良さそうです。
公式ドキュメントでは Notification のデリゲート処理に30秒以上かかった場合キャンセルされるとありますので、通信を介する処理などはユーザビリティとしても避けた方が良いかと思います。

&nbsp;
## Notification Content Extension の使い方！
### モジュールの追加
まず、アプリの Target に Notification Content Extension を追加します
下記画像を参考に、**TARGETS下の + をクリック**してください。

<a href="http://rc-code.info/wp-content/uploads/2018/ios-swift/notificationContentAppExtention/003.png"><img class="alignnone size-medium" src="http://rc-code.info/wp-content/uploads/2018/ios-swift/notificationContentAppExtention/003.png" alt="Xcode編集画像" width="800" height="630" /></a>

&nbsp;

続いて、下記画像を参考に **Notification Content Extension** を指定します。

<a href="http://rc-code.info/wp-content/uploads/2018/ios-swift/notificationContentAppExtention/004.png"><img class="alignnone size-medium" src="http://rc-code.info/wp-content/uploads/2018/ios-swift/notificationContentAppExtention/004.png" alt="Xcode編集画像" width="800" height="566" /></a>

&nbsp;

下記の様に、Extension用のモジュールが追加された事を確認しましょう。

<a href="http://rc-code.info/wp-content/uploads/2018/ios-swift/notificationContentAppExtention/005.png"><img class="alignnone size-medium" src="http://rc-code.info/wp-content/uploads/2018/ios-swift/notificationContentAppExtention/005.png" alt="Xcode編集画像" width="801" height="627" /></a>

&nbsp;
### Notification カテゴリの設定
Extensionディレクトリに含まれる **Info.plist** の **ExtensionCategory** をエクステンションを利用したい場合のカテゴリ名に変更します

<a href="http://rc-code.info/wp-content/uploads/2018/ios-swift/notificationContentAppExtention/006.png"><img class="alignnone size-medium" src="http://rc-code.info/wp-content/uploads/2018/ios-swift/notificationContentAppExtention/006.png" alt="Xcode編集画像" width="802" height="605" /></a>

&nbsp;

上記はカテゴリ名を **GENERAL** と設定していますが、これによりNotificationで送られてきたペイロードのカテゴリが **GENERAL** だった場合にエクステンションを利用するようになります。

下記はNotificationをコードから実行する場合のサンプルコードです。

```swift:

// カテゴリの生成
let category = UNNotificationCategory(identifier: "GENERAL", actions: [], intentIdentifiers: [], options: [])
// UNUserNotificationCenter にカテゴリを登録
UNUserNotificationCenter.current().setNotificationCategories([category])

// Notification の生成
let content = UNMutableNotificationContent()
content.title = title_textField.text
content.body = body_textField.text
content.categoryIdentifier = "GENERAL"

// 発火する時間を設定
let time = TimeInterval(0)
// 発火するためのタイマーを生成
let trigger = UNTimeIntervalNotificationTrigger.init(timeInterval: time, repeats: false)
// Notification のリクエストを生成
let request = UNNotificationRequest.init(identifier: "myNotification", content: content, trigger: trigger)
// リクエストを登録
UNUserNotificationCenter.current().add(request)

// 上記処理で、0秒後にカテゴリ "GENERAL" のNotificationが発火します
```

&nbsp;

下記はカテゴリが GENERAL に設定されたDNSのペイロードサンプルです

```swift:
{
    "aps" : {
        "category" : "GENERAL"
        "alert" : {
            "title" : "PushNotification Test",
            "body" : "hogehogehoge fugafugafuga"
        },
        "badge" : 3,
        "sound" : "chime.aiff"
    }
}
```

&nbsp;
### デフォルトコンテンツの表示設定
**UNNotificationExtensionDefaultContentHidden** を **Info.plist** の **NSExtensionAttributes配下** に追加し、デフォルトの通知コンテンツ（2つ目画像の Default interface 部分）を隠したければ、**Boolean = YES** を格納します。

<a href="http://rc-code.info/wp-content/uploads/2018/ios-swift/notificationContentAppExtention/007.png"><img src="http://rc-code.info/wp-content/uploads/2018/ios-swift/notificationContentAppExtention/007.png" width="800" height="605" alt="Xcode編集画像" class="alignnone size-medium" /></a>
&nbsp;

### インターフェースの操作設定
iOS12 から **UNNotificationExtensionUserInteractionEnabled** を **Info.plist** の **NSExtensionAttributes配下** に追加し、**Boolean = YES** を格納するで、CustomViewController で ** switch や button といったインタラクティブなインターフェースを実装する事が出来る **ようになりました。

<a href="http://rc-code.info/wp-content/uploads/2018/ios-swift/notificationContentAppExtention/008.png"><img src="http://rc-code.info/wp-content/uploads/2018/ios-swift/notificationContentAppExtention/008.png" width="800" height="605" alt="Xcode編集画像" class="alignnone size-medium" /></a>

これを設定しないと、デフォルトで用意されている Button 以外にインターフェースを実装しても反応しません。

デフォルトで用意されているビデオ制御用のインターフェースを利用する場合は下記のような実装になります。

```swift:

class NotificationViewController: UIViewController, UNNotificationContentExtension {

    @IBOutlet weak var emptyView: UIView!

    ~~~ 省略 ~~~

    let mediaPlayPauseButtonType: UNNotificationContentExtensionMediaPlayPauseButtonType = .overlay
    var mediaPlayPauseButtonFrame: CGRect {
        return CGRect(x: self.view.frame.width / 2 - emptyView.frame.width / 2, y: playerView.frame.height / 2 - emptyView.frame.height / 2, width: emptyView.frame.width, height: emptyView.frame.height)
    }

    public func mediaPlay() {
        ~~~ media制御 ~~~
    }

    public func mediaPause() {
        ~~~ media制御 ~~~
    }

    ~~~ 省略 ~~~
}
```
&nbsp;

### タイトルのカスタマイズ設定
**UNNotificationExtensionOverridesDefaultTitle** を **Info.plist** の **NSExtensionAttributes配下** に追加し、通知の Title をアプリ名以外に変えたければ、**Boolean = YES** を格納する

<a href="http://rc-code.info/wp-content/uploads/2018/ios-swift/notificationContentAppExtention/009.png"><img src="http://rc-code.info/wp-content/uploads/2018/ios-swift/notificationContentAppExtention/009.png" width="800" height="605" alt="Xcode編集画像" class="alignnone size-medium" /></a>

title を変更したい場合は、CustomViewController 内で下記のように実装する。

```swift:
func didReceive(_ notification: UNNotification) {
    self.title = "AAAAAA"
}
```
&nbsp;

これはNotification自体のtitle変更ではなく、CustomViewControllerを開いた際のtitleです。
<img src="http://rc-code.info/wp-content/uploads/2018/ios-swift/notificationContentAppExtention/010.png" width="400" height="160" alt="Notification着信時画像" class="alignnone size-medium" />　<img src="http://rc-code.info/wp-content/uploads/2018/ios-swift/notificationContentAppExtention/011.png" width="400" height="458" alt="Notification操作時画像" class="alignnone size-medium" />
&nbsp;

### コンテンツの作成
Extensionディレクトリに含まれる **NotificationViewController** と **MainInterface.storyboard** を編集し、表示したいコンテンツを作成します。

末尾に記載したサンプルでは、アプリ内に保存された動画を再生しています。
先に示した画像の右側が、ViewControllerの内容になります。

真ん中の再生マークは **iOS12以前で利用するためのデフォルトのインターフェース**です。
右下のButtonはiOS12から利用可能になった **UNNotificationExtensionUserInteractionEnabled** を有効にした際に操作可能になるカスタムViewのサンプルです。

## Ex. サンプル
<a href="https://github.com/uin010bm/UINNotificationContentAppExtension" rel="noopener" target="_blank">https://github.com/uin010bm/UINNotificationContentAppExtension</a>
&nbsp;

## Info. ドキュメント
<a href="https://developer.apple.com/documentation/usernotificationsui/customizing_the_appearance_of_notifications" target="_blank" rel="noopener" shape="rect">公式ドキュメント</a>
&nbsp;

## Info. サンプルテスト環境
Mac OS: 10.13.6
Xcode: Version 10.0
&nbsp;