NSPersistentCloudKitContainerの同期イベントを監視する

iOS13でNSPersistentCloudKitContainerが登場しましたが、その後もiOS14とiOS15で順調にAPIが拡充されています。

この記事では、これらの同期イベントを監視するための方法を解説します。

# 通知を利用して同期イベントを監視する
NSPersistentCloudKitContainerにはiOS14から同期イベントの監視のためのAPIが登場しています。

![スクリーンショット 2021-09-09 11.50.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114709/3052b0e0-8f87-278b-dd0e-0fa0f01ead62.png)
(Apple Developer Documentationの同期イベント監視オブジェクト一覧[^1])

この記事では、この中の[NSPersistentCloudKitContainer.eventChangedNotification](https://developer.apple.com/documentation/coredata/nspersistentcloudkitcontainer/3618808-eventchangednotification)を利用して通知により同期イベントを取得します。

## 事前条件
- CoreData with CloudKitがセットアップされたプロジェクトがある。

## Combineを利用して通知を受け取る
この記事ではCombineとして通知を受け取っていきます。`print`を使って内容を出力することで中身を確認していくので、ここでは`printObserver`という名前の`AnyCancellable`を定義していきます。

```swift
private var printObserver: AnyCancellable? = nil
```

同期イベントの発生は[NSPersistentCloudKitContainer.eventChangedNotification](https://developer.apple.com/documentation/coredata/nspersistentcloudkitcontainer/3618808-eventchangednotification)を通知名としてNotificationCenterの監視APIを利用することで監視することができます。今回はCombineの[publisher(for:object:)](https://developer.apple.com/documentation/foundation/notificationcenter/3329353-publisher)を利用していますが、[addObserver(_:selector:name:object:)](https://developer.apple.com/documentation/foundation/notificationcenter/1415360-addobserver)でも同様に監視を行えます。

この通知のuserInfoには[Event](https://developer.apple.com/documentation/coredata/nspersistentcloudkitcontainer/event)オブジェクトが格納されており、[NSPersistentCloudKitContainer.eventNotificationUserInfoKey](https://developer.apple.com/documentation/coredata/nspersistentcloudkitcontainer/3618813-eventnotificationuserinfokey)でアクセスすることによって取得できるように設計されています。

以下のサンプルでは取得したEventとそのインスタンスプロパティを1つずつ出力しています。

```swift
printObserver = notificationCenter
    .publisher(for: NSPersistentCloudKitContainer.eventChangedNotification)
    .sink(receiveValue: { notification in
        let event = notification.userInfo?[NSPersistentCloudKitContainer.eventNotificationUserInfoKey] as! NSPersistentCloudKitContainer.Event
        print("Reveiced cloudkit container's sync event: \(event)")
        print("type: \(event.type.rawValue)")
        print("identifier: \(event.identifier)")
        print("storeIdentifier: \(event.storeIdentifier)")
        print("succeeded: \(event.succeeded)")
        print("startDate: \(event.startDate)")
        print("endDate: \(event.endDate)")
        print("error: \(event.error)")
    })

```

この時、[event.type](https://developer.apple.com/documentation/coredata/nspersistentcloudkitcontainer/eventtype)を参照することで、ローカルの変更をクラウドに送信したのかクラウドの変更をローカルに受信したのかを判別することができます。

```swift
/// The type of event in a persistent CloudKit container, either setup, import, or export.
enum EventType {

    /// An event the persistent CloudKit container generates when setting up a store.
    case setup = 0

    /// An event the persistent CloudKit container generates when importing records into a store.
    case `import` = 1

    /// An event the persistent CloudKit container generates when exporting managed objects from a store.
    case export = 2
}
```

# どのように使うか？
例えば、イベント種類が`import`の際だけ、同期されたオブジェクトに対する事後処理を実装するなどが考えられます。(今自分で触ってどう使えるか確認しているところなので、まとまり次第追記します)

# まとめ
この記事では通知を利用して同期イベントを監視する方法をお伝えしました。

- NSPersistentCloudKitContainer.eventChangedNotificationを利用して同期イベントに関する通知を受け取る。
- userInfo?[NSPersistentCloudKitContainer.eventNotificationUserInfoKey]でイベントオブジェクトを取得する。
- event.typeでどの種類のイベントが発生したか判定する。

何かうまくAPIの使い方などあればコメントで教えてください！

[^1]: Apple inc., Monitoring Container Events, NSPersistentCloudKitContainer, https://developer.apple.com/documentation/coredata/nspersistentcloudkitcontainer, viewed: 2021/09/09