【iOS】LocalNotification使ってユーザーの復帰率を上げよう！

## はじめに

iOS Second Stage Advent Calendar 2015 10日目の記事です。

先日実装したiOSアプリで **LocalNotification**を使ったのでそれの紹介です。

## LocalNotificationについて

iOSにもAndroidにもPush通知がありますが、その他にアプリ側だけでNotificationを設定することもできます。

**iOS8**からはLocalNotificationを設定するのにも許可がいるようになりました。

それに関しては以下の記事に詳しく書いてあるので、そちらを参考にしてください。

[[iOS 8] UILocalNotification もユーザー認証が必要になりました ｜ Developers.IO](http://dev.classmethod.jp/references/ios-8-uilocalnotification-auth/)

**LocalNotification**の実装のサンプルを作ったので、参考になれば幸いです。

[AdventCalendar2015/SampleLocalNotification](https://github.com/ryokosuge/AdventCalendar2015/tree/master/SampleLocalNotification)

## 実装について

**LocalNotification**の実装のみをしたクラスを作成したのでそれを元に紹介していければと思います。

| Swift | OS |
|:--:|:--:|
| 2.1 | 8.0以上 |

### ソースコード

```swift:LocalNotificationManager.swift
//
//  LocalNotificationManager.swift
//  SampleLocalNotification
//
//  Created by kosuge on 2015/12/07.
//  Copyright © 2015年 RyoKosuge. All rights reserved.
//

import UIKit

/// LocalNotificationから起動したことを通知する
let DidOpenFromLocalNotification = "DidOpenFromLocalNotification"

/// userInfoの中のbodyを指すKey
let LocalNotificationUserInfoBodyKey = "body"

/// LocalNotification周りの処理を受け持つクラス
class LocalNotificationManager: NSObject {

    /// LocalNotificationを表示できるように登録処理をする
    static func registerNotification() {
        let application = UIApplication.sharedApplication()
        let settings = UIUserNotificationSettings(forTypes: [.Badge, .Alert, .Sound], categories: nil)
        application.registerUserNotificationSettings(settings)
    }
    
    /// LocalNotificationを許諾してもらった時の処理
    /// - parameter settings:       許可してもらったTyep
    /// - parameter application:    UIApplication
    static func didRegisterUserNotificationSettings(settings: UIUserNotificationSettings, application: UIApplication) {
        let allowedType = settings.types
        switch allowedType {
        case UIUserNotificationType.None:
            print("許可されなかったよ")
        default:
            print("許可されたよ")
        }
    }
    
    /// LocalNotificationを設定する
    /// - parameter date:       表示する時間
    /// - parameter alertBody:      表示する文言
    /// - parameter userInfo:       Notificationに渡したい情報([NSObject: AnyObject])
    static func scheduleLocalNotificationAtDate(date: NSDate, alertBody: String, userInfo: [NSObject: AnyObject]?) {
        
        /// 今より過去に設定させない
        /// すぐに通知してしまうため
        if date.timeIntervalSinceNow <= 0 {
            return
        }
        
        /// LocalNotificationの作成
        let localNotification = UILocalNotification()
        localNotification.fireDate = date
        localNotification.timeZone = NSTimeZone.localTimeZone()
        localNotification.alertBody = alertBody
        localNotification.userInfo = userInfo
        localNotification.alertAction = "開く"
        
        /// 設定
        UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
    }
    
    /// LocalNotificationをキャンセルする
    static func cancelAllLocalNotifications() {
        guard let localNotifications = UIApplication.sharedApplication().scheduledLocalNotifications else {
            /// 設定されていないから何もしない
            return
        }
        
        for localNotification in localNotifications {
            /// userInfoの中にユニークな値(例えばIDとか)を入れておけば何のためのLocalNotificationなのか判別がつく
            if let userInfo = localNotification.userInfo {
                /// あとはよしなに...
                print("userInfo = \(userInfo)")
            }
        }
        
        /// 全てを削除する
        UIApplication.sharedApplication().cancelAllLocalNotifications()
    }
    
    /// LocalNotificationから開かれた場合の処理
    /// - parameter localNotification:      受け取ったLocalNotification
    /// - parameter application:            UIApplication
    static func didReceiveLocalNotification(localNotification: UILocalNotification, application: UIApplication) {
        print("受け取ったよ")
        notifyReceivedLocalNotification(localNotification)
    }
    
    /// アプリが起動した時にLocalNotificationから開かれたどうかを確認する
    /// LocalNotificationから開かれた場合は処理をする
    /// - parameter launchOptions:   起動時の値
    /// - parameter application:        UIApplication
    static func didFinishedLaunchingOptions(launchOptions: [NSObject: AnyObject]?, application: UIApplication) {
        
        /// LocalNotificationから開かれた場合launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]にUILocalNotificationが代入されている
        guard let localNotification = launchOptions?[UIApplicationLaunchOptionsLocalNotificationKey] as? UILocalNotification else {
            /// LocalNotificationから開かれてないので何もしない
            return
        }
        
        notifyReceivedLocalNotification(localNotification)
    }
    
}

/// MARK : - private methods

extension LocalNotificationManager {
    
    /// LocalNotificationを受け取ったことを通知する
    /// - parameter localNotification:      受け取ったLocalNotification
    private static func notifyReceivedLocalNotification(localNotification: UILocalNotification) {
        guard let body = localNotification.alertBody else {
            return
        }
        
        let userInfo = [LocalNotificationUserInfoBodyKey: body]
        let notification = NSNotification(name: DidOpenFromLocalNotification, object: nil, userInfo: userInfo)
        let queue = NSNotificationQueue.defaultQueue()
        queue.enqueueNotification(notification, postingStyle: NSPostingStyle.PostWhenIdle)
    }
    
}
```

### 解説

ほぼコメントに書いたので、問題ないかと思っています...。（手抜きじゃないよ）

ということで使い方でも紹介します。

### 使い方

#### register

上にも書きましたが **iOS8**から **LocalNotification**にも許可が必要になりました。

なので登録処理が必要になります。

```swift:LocalNotificationManager#registerNotification
/// LocalNotificationを表示できるように登録処理をする
static func registerNotification() {
    let application = UIApplication.sharedApplication()
    let settings = UIUserNotificationSettings(forTypes: [.Badge, .Alert, .Sound], categories: nil)
    application.registerUserNotificationSettings(settings)
}
```

こちらのメソッドで登録処理をしています。

このメソッドを呼ぶと以下のアラートが表示されます。

![スクリーンショット 2015-12-08 0.41.46.png](https://qiita-image-store.s3.amazonaws.com/0/47302/ef5c855a-a40d-e883-6ed8-62d112931c54.png "スクリーンショット 2015-12-08 0.41.46.png")

お馴染みのあいつです。

余談ですが、iOS9からインストールするたびに聞かれるようになったのでデバッグがかなり捗りますね！

で、このアラートでOKを押してもらうとAppDelegateの`func application(application: UIApplication, didRegisterUserNotificationSettings notificationSettings: UIUserNotificationSettings)`のメソッドが呼ばれます。

```swift:AppDelegate.swift
/// application.registerUserNotificationSettings(settings)の許可をもらったら呼ばれる
func application(application: UIApplication, didRegisterUserNotificationSettings notificationSettings: UIUserNotificationSettings) {
    LocalNotificationManager.didRegisterUserNotificationSettings(notificationSettings, application: application)
}
```

それでとりあえずOKということにしています。（サンプルではそうですが作るものによっては色々やることがあるかと）

### scheduleLocalNotification

**LocalNotification**を設定するのに以下のメソッドを呼びます。

```swift:LocalNotificationManager#scheduleLocalNotificationAtDate
/// LocalNotificationを設定する
/// - parameter date:       表示する時間
/// - parameter alertBody:      表示する文言
/// - parameter userInfo:       Notificationに渡したい情報([NSObject: AnyObject])
static func scheduleLocalNotificationAtDate(date: NSDate, alertBody: String, userInfo: [NSObject: AnyObject]?) {
        
    /// 今より過去に設定させない
    /// すぐに通知してしまうため
    if date.timeIntervalSinceNow <= 0 {
        return
    }
        
    /// LocalNotificationの作成
    let localNotification = UILocalNotification()
    localNotification.fireDate = date
    localNotification.timeZone = NSTimeZone.localTimeZone()
    localNotification.alertBody = alertBody
    localNotification.userInfo = userInfo
    localNotification.alertAction = "開く"
        
    /// 設定
    UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
}
```

とまあNSNotificationを使ったことがある方には何の疑問もないかと思います。

一つだけ疑問があるとすれば`localNotification.alertAction`だと思います。

こちらはiPhoneがスリープしている時にAlertで **LocalNotification**が表示された時のロック解除の文言になります。

つまり **LocalNotification**の許可でAlertになっていないと意味をなさないものになっています。

あとはGitHubのサンプルを見てもらいたいなと思います。

何かわからないことがあったらコメントでも何でもいただけたらなと思います。

## 終わりに

サンプルを作るのがすごい勉強になるなと思いながら、世に自分の変数やメソッド名の命名センスが公開されることにとても恥ずかしさを覚えております。

そこらへんを詳しく教えていただける優しい人がいたらすごい嬉しいです。

サンプルは以下になります。

[AdventCalendar2015/SampleLocalNotification](https://github.com/ryokosuge/AdventCalendar2015/tree/master/SampleLocalNotification)

以上になります。

（僕が担当する）次回はSmartNewsのあそこのUIについて書きます。

## 参考

- [[iOS 8] UILocalNotification もユーザー認証が必要になりました ｜ Developers.IO](http://dev.classmethod.jp/references/ios-8-uilocalnotification-auth/)