iOS8でUILocalNotificationの通知を受け取る

iOSでのPush通知については話題にされることも多いので,ここではRemoteNotificationとLocalNotificationの違いなどについては特に触れません.

今回題材にするのはLocalNotificationのみです.

UILocalNotificationの通知を発行する


これはObjective-Cの時とさほど変化はない.

var notification = UILocalNotification()
notification.fireDate = NSDate()	// すぐに通知したいので現在時刻を取得
notification.timeZone = NSTimeZone.defaultTimeZone()
notification.alertBody = "message"
notification.alertAction = "OK"
notification.soundName = UILocalNotificationDefaultSoundName
UIApplication.sharedApplication().presentLocalNotificationNow(notification)

これでLocalNotificationが発行される.

と思いきや,なにやらWarnningを吐き出していた.

{fire date = Monday, September 1, 2014 at ...(中略)..., user info = (null)} with an alert but haven't received permission from the user to display alerts


こんな感じ.


これは調べれば出てくるのだけれど,iOS8からはregisterUserNotificationSettingsを最初に呼んであげないと,通知を登録しても発動する権限がないらしい.



というわけで,できればアプリケーション起動時にこいつを登録してやります.

func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {

     application.registerUserNotificationSettings(UIUserNotificationSettings(forTypes: UIUserNotificationType.Sound | UIUserNotificationType.Alert | UIUserNotificationType.Badge, categories: nil))

     return true
}

これで通知が許可されて,無事通知されるかと思います.

ちなみにアプリケーションの初回起動時に,「通知を許可しますか?」みたいな見慣れたアラートが出てくるので許可しておきましょう.


アプリケーション起動時の通知受信


通知を発行しているアプリケーション自体がフォアグラウンドにあるとき,LocalNotificationは発行されていますが,特に表示されたりはしません.

起動している時に通知を受け取るメソッドが,didReceiveLocalNotificationです.

func application(application: UIApplication, didReceiveLocalNotification notification: UILocalNotification) {

     var alert = UIAlertView()
     alert.title = "Message"
     alert.message = notification.alertBody
     alert.addButtonWithTitle(notification.alertAction)
     alert.show()
}

このように,LocalNotificationを受け取ったらAlertを出してやります.
割りと普通のアプリっぽいですね.


でもこのdidRecieveLocalNotification,実はアプリケーションがフォアグラウンドのときだけ呼ばれます.

Objective-C関連の情報をあさっていると,LocalNotificationが発行されればアプリケーションがバックグラウンド実行であってもdidRecieveLocalNotificationが呼ばれるという記述をよく目にしました.

ところがiOS8あたりでは,この挙動が変わっているようで,デバッグしてみてもどうやっても呼ばれていません.

※2014/10/28確認で,上記のメソッドでバックグラウンド時の通知を受け取れるように変更されていました.修正記事をあげています.

iOS8のUILocalNotificationに関して修正 - PartyIX



バックグラウンド実行時の通知受信


ずばり,handleActionWithIdentifierを使います.
アプリケーションをバックグラウンドで実行しているときには,LocalNotificatioがiPhone画面の上部にチラっと出てきます.
これをタップした際には,バックグラウンド実行だったアプリケーションがフォアグラウンドに来ます.

そのとき,applicationWillEnterForegroundなどと同じく呼ばれるのが,このhandleActionWithIdentifierです.

func application(application: UIApplication, handleActionWithIdentifier identifier: String?, forLocalNotification notification: UILocalNotification, completionHandler: () -> Void) {
     // ここでBackgroundから復帰したときの処理
}

今回話したのはアプリケーションが実行されているのが前提


なおこれらの処理はすべてアプリケーションがフォアグラウンドなりバックグラウンドなりで実行されているときだけ呼ばれます.


LocalNotificationは通知を発動させる時間を指定できます.
そのため,アプリケーション自体のタスクをkillした後でも,通知を予約しておけば通知が来ます.

その際は,上記のメソッドではなく,アプリケーションの起動処理が走るため,didFinishLaunchingWithOptionsが呼ばれます.

func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {
     if (launchOptions != nil) {
          var notification:UILocalNotification? = launchOptions.objectForKey(UIApplicationLaunchOptionsLocalNotificationKey) as? UILocalNotification
	  if (notification != nil) {
	     // 通知を受け取った時の処理

	     // 最後に通知を消す
	     UIApplication.sharedApplication().cancelLocalNotification(notification)
	  }
     }
}


というようにしてやると,見事通知を受け取れます.


LocalNotificationが発動するのはだいたいこの3パターンだと思うので,このくらいを記述しておけば問題なさそうですね.



参考:
cocoa touch - Ask for User Permission to Receive UILocalNotifications in iOS 8 - Stack Overflow
uilocalnotification - application:didReceiveLocalNotification never called on ios 8 - Stack Overflow