[Swift3]ローカル通知の実装方法

今回はローカル通知の実装について書いていきます。

# ローカル通知とは
Appleの[公式ドキュメント(日本語)](https://developer.apple.com/jp/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/Introduction.html)には次のように書いています。

> ローカル通知とリモート通知の2つを「ユーザ通知」と総称し、 ... フォアグラウンドで動作していないアプリケーションが、ユーザに何らかの情報を伝えるための仕組みです。
ローカル通知かリモート通知かによらず、その見た目や音声は同じです。それらの通知は、警告メッセージまたはアプリケーションアイコンのバッジを表示できます。

つまりはアプリを使っていない状態のユーザーに対しても何かしらの訴求をすることができる機能ですね。

ユーザーがアプリを使っていない時にユーザーへ任意のメッセージを送信できるため、アプリのリテンション率の改善や、アクティブユーザー数の改善などに貢献する機能と言えるでしょう。

# できたもの
実施に実装してできたものがこちら。

![FullSizeRender.jpg](https://qiita-image-store.s3.amazonaws.com/0/143619/9fd1375d-ada2-8ba3-eda4-3de0dda6f0db.jpeg)

# ローカル通知を表示させる際の条件
ローカル通知を出力させることができる条件は複数あります。

1. 通知を登録したタイミングを起点として任意の時間経過後に通知
2. カレンダーの任意の日時で通知
3. 任意の位置情報(Location)と現在位置が一致した時に通知

# 実装方法
1. ユーザーに対して通知表示許諾をとる
2. トリガーを設定
3. 通知の表示内容についての設定
4. 通知を登録

### 1. ユーザーに対して通知表示許諾をとる
まずユーザーへ通知を表示するためには、ローカル通知とプッシュ通知問わず通知表示の許諾をユーザーから得る必要があります。

新しいアプリをインストールした時などに見るダイアログがありますが、あれがその `通知表示の許諾をユーザーから得る` ためのダイアログです。

![FullSizeRender 2.jpg](https://qiita-image-store.s3.amazonaws.com/0/143619/8d633e56-4f5e-2e10-9ee7-f18d5d647913.jpeg)

このダイアログを表示した際に、[APNs(Apple Push Notification Service)](https://developer.apple.com/jp/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/ApplePushService.html)に端末情報が登録されるようになっています。

このダイアログは以下のコードを任意のタイミングで実行することで表示することが可能です。

```swift
if #available(iOS 10.0, *) {
    // iOS 10
    let center = UNUserNotificationCenter.current()
    center.requestAuthorization(options: [.badge, .sound, .alert], completionHandler: { (granted, error) in
        if error != nil {
            return
        }
        
        if granted {
            print("通知許可")
            
            let center = UNUserNotificationCenter.current()
            center.delegate = self
            
        } else {
            print("通知拒否")
        }
    })
    
} else {
    // iOS 9以下
    let settings = UIUserNotificationSettings(types: [.badge, .sound, .alert], categories: nil)
    UIApplication.shared.registerUserNotificationSettings(settings)
}
```

また個人的な意見ですが、この通知許諾ダイアログをアプリの初回起動時(例えば`(_application:didFinishLaunchingWithOptions:)`など)で表示すると、**通知の許諾率が低くなってしまう可能性が高い**です。

最近では通知を表示するアプリが多くなっており、通知を拒否するユーザーも増えているため、ユーザーがなぜ通知を受け取るかが明確になっていないので、とりあえず通知をオフにしてしまう傾向にあるためです。

それを回避するため、ダイアログを表示する際のおすすめタイミングは

1. アプリの機能を一通り使った後 
2. アプリ内でユーザーが何かしらの目標を達成した後
3. このアプリで通知するものが何かを説明した後

などが例として挙げられます。

1と2はアプリへの関心が高まっているタイミング、３はそもそも通知する内容をユーザーへ明示することで通知を受信することに納得してもらうなどの狙いがあります(最近だとこのダイアログを表示する前に自前のダイアログを表示するアプリも増えていますね)。

ぜひみなさんも実装される際には、この表示タイミングについても考慮してみて下さい。


### 2. トリガーを設定
先の`ローカル通知を表示させる際の条件`にも書いたあったそれぞれの条件毎にトリガーを設定していきます。

```swift
// 設定に必要なクラスをインスタンス化
var notificationTime = DateComponents()
let trigger: UNNotificationTrigger

// 12時に通知する場合
notificationTime.hour = 12
notificationTime.minute = 0
trigger = UNCalendarNotificationTrigger(dateMatching: notificationTime, repeats: false)

/*その他の設定*/

// 設定したタイミングを起点として1分後に通知したい場合
trigger = UNTimeIntervalNotificationTrigger(timeInterval: 60, repeats: false)
```

この時に`UNCalendarNotificationTrigger(repeats:)`の引数を`true`にすると、指定された条件に合致した際に毎回通知を行うようになり、`false`にすると1度だけ通知を行います。

### 3. 通知の表示内容についての設定
次に通知の表示内容を決定する設定をします。

```swift
// UNMutableNotificationContentクラスをインスタンス化
let content = UNMutableNotificationContent()

// 通知のメッセージセット
content.title = ""
content.body = "食事の時間になりました！"
content.sound = UNNotificationSound.default()
```

この時、`.title`と`.sound`は任意なので設定をする必要はないのですが、`.body`は必須項目なのでここで文字列の指定が無いとそもそもメッセージ表示されませんのでご注意を。

### 4. 通知を登録
最後にこれまで設定した内容を登録して`UNUserNotificationCenter`へ追加します。

```swift
// 通知スタイルを指定
let request = UNNotificationRequest(identifier: "uuid", content: content, trigger: trigger)

// 通知をセット
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
```

この処理はアプリがバックグラウンドへ遷移した際に登録したいので、`applicationDidEnterBackground(_ application:)`内で実装します。

```AppDelegate.swift
func applicationDidEnterBackground(_ application: UIApplication) {
    
    //　通知設定に必要なクラスをインスタンス化
    let trigger: UNNotificationTrigger
    let content = UNMutableNotificationContent()
    var notificationTime = DateComponents()
    
    // トリガー設定
    notificationTime.hour = 12
    notificationTime.minute = 0
    trigger = UNCalendarNotificationTrigger(dateMatching: notificationTime, repeats: false)
    
    // 通知内容の設定
    content.title = ""
    content.body = "食事の時間になりました！"
    content.sound = UNNotificationSound.default()

    // 通知スタイルを指定
    let request = UNNotificationRequest(identifier: "uuid", content: content, trigger: trigger)
    // 通知をセット
    UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)

}
```
これで一度アプリを立ち上げてからバックグラウンドへ遷移させることで通知を表示することができます。

また、今回の実装は下記を参考にさせていただきました。

わかりやすい記事をありがとうございますm(_ _)m

[【iOS 10】User Notifications Frameworkで通知をカスタムしてみる](http://atamo-dev.hatenablog.com/entry/2016/10/20/022250)
[【Swift3.0】とりあえずローカルの通知をとばす](http://sugicom.hatenablog.com/entry/2017/01/15/221721)