最近になったふたたびXcodeに触れる機会ができました。ちょっとしたアプリを作る際に通知機能を付けたくて、でもサーバーがまだ無いのでローカル通知を実装することにしました。
単純なアラームくらいだったら結構簡単に作れちゃうんですね。使い方をまとめてみました。
ローカル通知
iOSには通知の方法が2種類あって、1つはリモート通知と呼ばれるサーバー側から各端末に情報をプッシュする方式。LINEみたいにメッセージが飛んできたら画面に表示されたりします。
もう1つがローカル通知で、こっちはアプリからiOSの画面に情報を表示できるようになる方法で、サーバーも要らずプログラムだけで実現できます。カレンダーを作って予定のある日を教えてくれるようなアプリなどに使えそうです。
今回はサーバーが無いのでローカル通知をやってみることにしました。
ローカル通知の使い方
プロジェクトの作成
まずは新しいプロジェクトを作成します。テンプレートは「Single View App」を選択してください。LanguageをSwiftにして「Use Core Data」のチェックは不要です。
AppDelegateへの初期設定
先にローカル通知を使うための設定をAppDelegateの中に書いてしまいます。最初にUserNotificationsをインポートします。
import UserNotifications
次に通知機能を使いますよ、という許可のダイアログを表示する部分をdidFinishLaunchingWithOptionsに追加します。もちろん許可しないと通知も出ません。次のコードを書くまではエラーが表示されますが、そのままで大丈夫です。
// 通知許可の取得 UNUserNotificationCenter.current().requestAuthorization( options: [.alert, .sound, .badge]){ (granted, _) in if granted{ UNUserNotificationCenter.current().delegate = self } }
最後は通知を受け取ったときの処理をAppDelegateクラスの後ろにExtensionとして足してあげます。AppDelegateクラスのカッコが閉じた後に追加するので気をつけてくださいね。これだけでアプリが起動中でも、他のアプリを使っている最中でも通知が飛んでくるようになります。便利。
extension AppDelegate: UNUserNotificationCenterDelegate{ func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { // アプリ起動中でもアラートと音で通知 completionHandler([.alert, .sound]) } func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { completionHandler() } }
画面側の作成(事前準備)
通知機能を使うための下準備が終わったので、今度はViewControllerに画面側の処理を書いていきます。通知を使うのでこちらもUserNotificationsをインポートします。
import UserNotifications
これ通知には必要ないのですが、日時を表示するときのために入れときます。viewDidLoadの上あたりに1行宣言を足して、viewDidLoadの中で日付のフォーマットを設定しておきます。
let dateFormatter = DateFormatter()
// 日付フォーマット dateFormatter.locale = Locale(identifier: "en_US_POSIX") dateFormatter.timeStyle = .short dateFormatter.dateStyle = .short dateFormatter.locale = Locale(identifier: "ja_JP")
次に処理を実行するボタンを作成していきます。ストーリーボード上で作る説明が面倒なのでviewDidLoad内でコードで描きます。上の日付フォーマット設定の下あたりに貼り付ければオッケーです。もちろんストーリーボード上にボタンを4つ置いてアクション接続しても良いですよ。上から日時指定通知、タイマー通知、そして実行待ちの通知を取得するボタン、実行済の通知を取得するボタンになります。まだボタンを押したときに呼ばれる関数を作っていないのでエラーが表示されますが、これから作っていくのでお気になさらずに。
// 日時指定通知のボタン作成 let buttonCalendar = UIButton() buttonCalendar.frame = CGRect(x:10, y:40, width:200, height:50) buttonCalendar.setTitle("日時指定通知", for:UIControl.State.normal) buttonCalendar.backgroundColor = UIColor.red buttonCalendar.addTarget(self, action: #selector(ViewController.buttonCalendarTouchUpInside(sender:)), for: .touchUpInside) self.view.addSubview(buttonCalendar) // タイマー通知のボタン作成 let buttonTimer = UIButton() buttonTimer.frame = CGRect(x:10, y:100, width:200, height:50) buttonTimer.setTitle("タイマー通知", for:UIControl.State.normal) buttonTimer.backgroundColor = UIColor.blue buttonTimer.addTarget(self, action: #selector(ViewController.buttonTimerTouchUpInside(sender:)), for: .touchUpInside) self.view.addSubview(buttonTimer) // 実行待ち通知一覧 let buttonPendingList = UIButton() buttonPendingList.frame = CGRect(x:10, y:160, width:200, height:50) buttonPendingList.setTitle("実行待ち通知一覧", for:UIControl.State.normal) buttonPendingList.backgroundColor = UIColor.orange buttonPendingList.addTarget(self, action: #selector(ViewController.buttonPendingListTouchUpInside(sender:)), for: .touchUpInside) self.view.addSubview(buttonPendingList) // 実行済み通知一覧 let buttonDeliveredList = UIButton() buttonDeliveredList.frame = CGRect(x:10, y:220, width:200, height:50) buttonDeliveredList.setTitle("実行済み通知一覧", for:UIControl.State.normal) buttonDeliveredList.backgroundColor = UIColor.purple buttonDeliveredList.addTarget(self, action: #selector(ViewController.buttonDeliveredListTouchUpInside(sender:)), for: .touchUpInside) self.view.addSubview(buttonDeliveredList)
日時指定通知
では日時指定通知のボタンの処理を作っていきます。簡単に表示内容と時間をセットして登録するだけです。identifierの部分は何でも良いのですが、ここではユニークなIDを発行しています。このIDで通知をキャンセルしたり上書きしたりといった制御ができます。
日付をデータコンポーネントに変換して設定するので、その前にボタンを押した時間から5分後を計算しています。
@objc func buttonCalendarTouchUpInside(sender : UIButton) { print("buttonCalendarTouchUpInside") // ローカル通知のの内容 let content = UNMutableNotificationContent() content.sound = UNNotificationSound.default content.title = "ローカル通知テスト" content.subtitle = "日時指定" content.body = "日時指定によるタイマー通知です" // ローカル通知実行日時をセット(5分後) let date = Date() let newDate = Date(timeInterval: 5*60, since: date) let component = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute], from: newDate) // ローカル通知リクエストを作成 let trigger = UNCalendarNotificationTrigger(dateMatching: component, repeats: false) // ユニークなIDを作る let identifier = NSUUID().uuidString let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger) // ローカル通知リクエストを登録 UNUserNotificationCenter.current().add(request){ (error : Error?) in if let error = error { print(error.localizedDescription) } } }
タイマー通知
今度はタイマー通知を作成する処理を書いていきます。ボタンを押した10秒後に通知が表示されるようにします。日付の代わりにタイマーをセットするだけでやっていることは日時指定と同じです。
@objc func buttonTimerTouchUpInside(sender : UIButton) { print("buttonTimerTouchUpInside") // ローカル通知のの内容 let content = UNMutableNotificationContent() content.sound = UNNotificationSound.default content.title = "ローカル通知テスト" content.subtitle = "タイマー通知" content.body = "タイマーによるローカル通知です" // タイマーの時間(秒)をセット let timer = 10 // ローカル通知リクエストを作成 let trigger = UNTimeIntervalNotificationTrigger(timeInterval: TimeInterval(timer), repeats: false) let identifier = NSUUID().uuidString let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger) UNUserNotificationCenter.current().add(request){ (error : Error?) in if let error = error { print(error.localizedDescription) } } }
実行待ち通知一覧
処理としてはこれで完成ですが、登録内容を確認するためのボタンも作ったのでその中身も作っていきます。まずは実行待ち通知一覧から。タイマーと時間指定で取得方法がちょっと違いますね。
@objc func buttonPendingListTouchUpInside(sender : UIButton) { print("<Pending request identifiers>") let center = UNUserNotificationCenter.current() center.getPendingNotificationRequests { (requests: [UNNotificationRequest]) in for request in requests { print("identifier:\(request.identifier)") print(" title:\(request.content.title)") if request.trigger is UNCalendarNotificationTrigger { let trigger = request.trigger as! UNCalendarNotificationTrigger print(" <CalendarNotification>") let components = DateComponents(calendar: Calendar.current, year: trigger.dateComponents.year, month: trigger.dateComponents.month, day: trigger.dateComponents.day, hour: trigger.dateComponents.hour, minute: trigger.dateComponents.minute) print(" Scheduled Date:\(self.dateFormatter.string(from: components.date!))") print(" Reperts:\(trigger.repeats)") } else if request.trigger is UNTimeIntervalNotificationTrigger { let trigger = request.trigger as! UNTimeIntervalNotificationTrigger print(" <TimeIntervalNotification>") print(" TimeInterval:\(trigger.timeInterval)") print(" Reperts:\(trigger.repeats)") } print("----------------") } } }
実行済み通知一覧
こちらは実行が終わった通知の一覧です。取ってくる元が違いますが、やってることはほぼ同じです。
@objc func buttonDeliveredListTouchUpInside(sender : UIButton) { print("<Delivered request identifiers>") let center = UNUserNotificationCenter.current() center.getDeliveredNotifications { (notifications: [UNNotification]) in for notification in notifications { print("identifier:\(notification.request.identifier)") print(" title:\(notification.request.content.title)") if notification.request.trigger is UNCalendarNotificationTrigger { let trigger = notification.request.trigger as! UNCalendarNotificationTrigger print(" <CalendarNotification>") let components = DateComponents(calendar: Calendar.current, year: trigger.dateComponents.year, month: trigger.dateComponents.month, day: trigger.dateComponents.day, hour: trigger.dateComponents.hour, minute: trigger.dateComponents.minute) print(" Scheduled Date:\(self.dateFormatter.string(from: components.date!))") print(" Reperts:\(trigger.repeats)") } else if notification.request.trigger is UNTimeIntervalNotificationTrigger { let trigger = notification.request.trigger as! UNTimeIntervalNotificationTrigger print(" <TimeIntervalNotification>") print(" TimeInterval:\(trigger.timeInterval)") print(" Reperts:\(trigger.repeats)") } print("----------------") } } }
これで日時指定通知とタイマー通知が使えるようになりました!意外と簡単ですが、アプリとは同期せずに不意にやって来る感じなので、ちょっと注意が必要ですね。
サンプルはこちらにあります。
おわりに
通知にはもう1つ位置情報というのもあるのですが、それはGPSの設定もあるので別の機会にやってみようと思います。
[amazonjs asin=”4798054429″ locale=”JP” title=”現場のためのSwift4 Swift4.1+Xcode9.3対応”]