UNNotificationContentExtension で 画像をダウンロードしたり、AppGroupでアプリ本体と情報共有したり

httpで通信してて工数殺したので自戒の念を込めて書いてたんですが、
Extensionで画像をダウンロードしてーってQiita無いなって思ったのでそれも含めて書いてます。
なお、今更HTTPを使うなというのがAppleの意向なのでそれに従う必要があります。[^kamokamo]

あと、AppGroupsをこのExtensionで使ったってQiitaもないのでTIPSにいれておきました。

[^kamokamo]: ATS使えば通信できるかも??かも??でも明日には使え無くなる可能性有るかも??

# httpsを使わないと
`UNErrorDomain`が Throw されます。

# 前提

* 既に `UNNotificationContentExtension` を使ったものが実装されてる



# 実装
* 画像ファイルは存在している.サーバー側に問題は無い。
* Extensionじゃない本体では、 `App Transport Security Settings` を設定している
    * 本体側ではhttp通信をしている箇所がある
* `import UserNotifications` `import UIKit` だけ使いたい。
* 画像(png/jpg)をダウンロードするコードは以下

```myNotificationExtension.swift
import UserNotifications
import UIKit

class NotificationService: UNNotificationServiceExtension {
    var contentHandler: ((UNNotificationContent) -> Void)?
    var bestAttemptContent = UNMutableNotificationContent.init()

    // ...
    // 割愛
    // ...


    override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
      self.contentHandler = contentHandler
      bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)!
      let host = "https://abcdefg-hogetarou.tv" // httpだと出来ない!!!
      let image = "/terebibangumi.png"
      let fileManager = FileManager.default
      let tmpSubFolderName = ProcessInfo.processInfo.globallyUniqueString
      let tmpSubFolderURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(tmpSubFolderName, isDirectory: true)
      let session = URLSession(configuration: URLSessionConfiguration.default)
      let task = session.dataTask(with: URL(string: host + image)!, completionHandler: { (data, response, error) in
          do {
              self.bestAttemptContent.title = "\(self.bestAttemptContent.title)"
              try fileManager.createDirectory(at: tmpSubFolderURL, withIntermediateDirectories: true, attributes: nil)
              let imageFileIdentifier = "banbumi.png"
              let fileURL = tmpSubFolderURL.appendingPathComponent(imageFileIdentifier)
              try data?.write(to: fileURL)
              let imageAttachment = try UNNotificationAttachment.init(identifier: imageFileIdentifier, url: fileURL, options: nil)
              self.bestAttemptContent.attachments = [imageAttachment]
          } catch let e {
              self.bestAttemptContent.body = "\(String.init(describing: e))"
          }
          contentHandler(self.bestAttemptContent)
      })
      task.resume() 
      // ....
      // 割愛
      // ....
```

☝️ 色んな者を `do` で囲むのは `Exception` 投げられたときに追いづらくなる可能性があるので、上記のようにせずきちんと分けることをオススメします。

# TIPS

* エラーの時にタイトルを変えられるが、ユーザーにエラーがおこってるな?と思われてはいけない(と思うので)タイトルの最後に `.` とか `,` とか付けてデバッグデバッグ出来るようにしてる。
    * 上記では、 eをそのままbodyにいれている。これは開発用なので参考にする人がいれば注意.
* 画像がhttp通信が原因で、Exceptionが投げられると `UNErrorDomain` で `Invalid attachment file URL` みたいなのが届く。HTTPSを使えば解消できる。


## アプリ本体と情報共有
AppGroupsを使えば、アプリ本体から情報をもらうことができる。

###　1. Developer Center で App Groups を定義

![AppGroup.png](https://qiita-image-store.s3.amazonaws.com/0/93918/ddb406c0-e886-d720-e374-20a86e8f064c.png)


### 2. Xcodeで機能を有効にして、AppGroupを追加する
![Xcode.png](https://qiita-image-store.s3.amazonaws.com/0/93918/14c737eb-e13a-a799-8f78-914e5ad3950f.png)

### 3. 実装する

取得する方法： 

```myNotificationExtension.swift
let groupName = "group.terebi.dayo"
if let userDefaults = UserDefaults(suiteName: groupName) {
    let key = "video_killed_the radio_star"
    userDefaults.synchronize() //念のため
    let savedCount = userDefaults.integer(forKey: key) // 取れる yay
    // 割愛
```

設定する方法:

```Interactor.swift
let groupName = "group.terebi.dayo"
if let userDefaults = UserDefaults(suiteName: groupName) {
    let key = "video_killed_the radio_star"
    userDefaults.set(0, forKey: key) // 設定できる yay
    userDefaults.synchronize() //念のため
}
```





# 最後に

3ヶ月前にも同じ事をやってこの現象の原因は知っていたが、今回も1日掛かってしまった。
ちなみに前回は3日間かかった。
自戒の念を込めてTIPSを残す。