はじめに
前回はCloudKitを使ってクラウド経由でデータの共有を実現してみました。想像以上に簡単にできて驚きです。
とはいうものの、XCodeのコンソールに大量のログが表示されているのが気になります。これを放置して良いものかどうか。
このログの中で気になったpush notifications
に挑戦してみようという次第です。
ログを流し読みする
ログは大量ですが、時系列で見ると1回で一気に吐き出されています。
区分っぽいものは、
- SimulatedCrash:
- CoreData: warning: CoreData+CloudKit:
- CoreData: debug: CoreData+CloudKit:
- CoreData: CloudKit: CoreData+CloudKit:
の4区分ですね。
この内、気になるのは最初の2つ。後半2つはおそらく参考情報のようなものだと思います。バックグラウンドでデータの同期を取りましたよ、とか同期した内容はこうですよ、とか。
SimulatedCrashを詳しく見る
何を指摘されたのか
CloudKit push notifications require the 'remote-notification' background mode in the info plist
CloudKit push notifications
なんて、前回の実装で見覚えが無いんですけどね。
ちなみにCloudKitでPush通知に関するもののようです。データの変更が起きたときに各端末に情報が飛ぶのか……
push notificationsを調べる
まぁ、指摘されるということは何かあるのでしょう。そういえば、どこかで見たような気がする。
居た!
push notificationsが無いときはどうなるのか
試しに、Capabilities
から削除して実行してみました。
結果は変わらず。CloudKitを有効にした時点で、裏でpush notifications
を使う前提になっているんでしょうか。
CloudKit push notificationsを実装する
せっかくです。この際実装してみましょう。
push notificationsを元に戻す
さっき消してしまったので、もう一度有効にしておきます。
Background Modesを設定する
SimulatedCrash
でbackground mode
という文言が出てましたね。
CloudKit push notifications require the 'remote-notification' background mode in the info plist
Capabilities
に追加して、Remote notification
にチェックを入れておきましょう。
ユーザに許可を求める
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound], completionHandler: { authorized, error in
if authorized {
DispatchQueue.main.async(execute: {
application.registerForRemoteNotifications()
})
}
})
私こんなことしますけど良いですか表明と同意確認ですね。アプリ起動時に行ったほうが良いので、AppDelegate.swift
のdidFinishLaunchingWithOptions
に入れました。
import UserNotifications
をお忘れなく。
通知を受けたときの処理を実装する
UNUserNotificationCenterDelegate
プロトコルを実装します。それぞれでやっていることの解釈はおそらく有ってるような気がする。
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
//通知を表示しようとするときの処理をここへ
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
//通知を受け取ったときの処理をここへ
}
通知を出すときの処理を実装する
受けとりができるようになれば次は通知を出す処理です。
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let subscription = CKQuerySubscription(recordType:{レコードタイプ}, predicate: NSPredicate(format: "TRUEPREDICATE"), options: [.firesOnRecordUpdate,.firesOnRecordCreation,.firesOnRecordDeletion])
let info = CKSubscription.NotificationInfo()
info.titleLocalizationKey = "%1$@"
info.titleLocalizationArgs = ["name"]
info.alertLocalizationKey = "%1$@"
info.alertLocalizationArgs = ["attendance_time"]
info.shouldBadge = true //通知数をアプリアイコンの右肩に出すかどうか
info.soundName = "default" //通知音はどうするか
subscription.notificationInfo = info
CKContainer.default().privateCloudDatabase.save(subscription, completionHandler: { subscription, error in
if error == nil {
} else {
}
})
}
レコードタイプはCloudKit Dashboardにて確認を!
predicate
には通知を発出したいレコードの条件NSPredicate
を指定します。
通知で表示する情報はCKSubscription.NotificationInfo()
に詰め込みます。
info.titleLocalizationKey = "%1$@"
info.titleLocalizationArgs = ["name"]
についてですが、titleLocalizationArgs
にはエンティティのフィールド名を指定します。暗号の如きtitleLocalizationKey
の値ですが、これはArgs1つ目のフィールドの値を示しています。
info.titleLocalizationKey = "%2$@"
info.titleLocalizationArgs = ["name", "address"]
このようにすると、Args2つ目のフィールドの値を指すことになります。
いよいよ実行
ワクワク
ユーザへの同意
これはクリア
通知が来るか!?
結果:来ませんでした。
クラウドにはデータが保存されていましたが、通知が飛ばない。ログを見ると1 error
の文字が。
エラー修正(2件)
フィールド名を間違えていた
info.titleLocalizationKey = "%1$@"
info.titleLocalizationArgs = ["name"]
CloudKit Dashboardを見るとこのようなフィールドはありませんでした。
早合点よくない。xcdatamodeld
ファイルで設定したフィールド名と若干違っていました。レコードタイプの確認のときにセットで見ておくべきでしたね。
XCode先生に面倒事を任せっきりにしてしまったゆえのポカミスです。先生との意思疎通大事。
ターゲットとするデータベースを間違えていた
今参照しているデータベースはプライベートの方です。
CKContainer.default().publicCloudDatabase.save(subscription, completionHandler: { subscription, error in
おわかりいただけただろうか。正しくは、
CKContainer.default().privateCloudDatabase.save(subscription, completionHandler: { subscription, error in
です。
リベンジします
Insertボタンをポチッとな。
でました!
気になる
publicCloudDatabaseの扱い方
メソッド上はpublicCloudDatabase
の方も扱えそうな雰囲気がします。今回はエラーが出てしまいましたが、これの対応も見ていきたいと思います。
ユーザにイベントの一斉通知とかできそう。
Background fetchとな!
そっと裏でローカルDBの更新とかできそう。
確認の際に表示する文言を設定する
さっと流しましたが、おそらくinfo.plist
ファイルで設定できるはず。