On the WWDC 2019, apple as Core Data with a major update – NSPersistentCloudKitContainer is introduced. This means that without having to write a lot of code, using Core Data with CloudKit allows users to seamlessly access the Data in their apps across all of their Apple devices.

Core Data provides powerful object graph management capabilities for developing applications with structured Data. CloudKit allows users to access their data from every device logged into their iCloud account, while providing a backup service that is always available. Core Data with CloudKit combines the benefits of local persistence with cloud backup and network distribution.

In 2020 and 2021, Apple will continue to strengthen Core Data with CloudKit, adding public database synchronization and shared database synchronization on the basis of the initial support for private database synchronization.

In a few posts, I’ll introduce you to Core Data with CloudKit, debugging tips, console Settings, and try to dig deeper into its synchronization mechanisms.

Limitations of Core Data with CloudKit

  • Can only run in Apple’s ecology

    Unlike other cross-platform solutions, Core Data with CloudKit only works in the Apple ecosystem and only serves users in the Apple ecosystem.

  • High test threshold

    You need an Apple Developer Program account to access CloudKit services and the development team’s CKContainer during development. In addition, the performance on the simulator is far less reliable than on the real thing.

Benefits of Core Data with CloudKit

  • Almost free

    Developers rarely have to pay extra for web services. The private database is stored in the user’s personal iCloud space, and the capacity of the public database is automatically increased as the number of application users increases, up to 1 PB of storage, 10 TB of database storage, and 200 TB of traffic per day. Almost free, since Apple takes 15-30% of the app’s revenue.

  • security

    On the one hand, Apple ensures the security of users’ data by means of sandbox containers, data reservoir isolation, encrypted fields, authentication and other technologies. On the other hand, given Apple’s long-established reputation as a privacy defender among users, using Core Dat with CloudKit can add even more trust to your app.

    In fact, it was after seeing this feature at WWDC2019 that I got the impetus to develop health note — to keep data private while preserving it for the long term.

  • High integration and good user perception

    Authentication and distribution are insensitive. Users don’t need to make any additional logins to enjoy the full functionality.

Core Data

Core Data was born in 2005, and its predecessor, EOF, was recognized by many users in 1994. Over the years, Core Data has matured considerably. As an object graph and persistence framework, almost every tutorial will tell you not to treat it like a database, and not to treat it like an ORM.

Core Data includes, but is not limited to, serialized version management, object lifecycle management, object graph management, SQL isolation, change processing, persistent Data, Data memory optimization, and Data query.

Core Data offers a lot of features, but it’s not very beginner friendly and has a steep learning curve. In recent years, Apple has taken note of this issue by adding a PersistentContainer that makes Stack creation much easier. The advent of SwiftUI and Core Data templates makes it easier for beginners to use the power of SwiftUI in their projects.

CloudKit

For several years after Apple launched iCloud, developers were unable to integrate their applications with it. This was not addressed until Apple introduced the CloudKit framework in 2014.

CloudKit is a collection of databases, file storage, and user authentication systems that provide a mobile data interface between applications and iCloud containers. Users can access data stored on iCloud across multiple devices.

CloudKit’s Data types and internal logic are quite different from those of Core Data, and some compromise or processing is required to convert the Data objects of the two. In fact, when CloudKit came out, there was a strong desire from developers to make it easy to switch between the two. Prior to the launch of Core Data with CloudKit, third-party developers had provided solutions for synchronizing objects from Core Data or other Data, such as realms, to CloudKit, and most of these solutions are still supported.

In 2019, Apple finally offered its own solution, Core Data with CloudKit, which builds on previous persistent history tracking features.

Core Data objects vs CloudKit objects

Both frameworks have their own underlying object types that cannot be mapped to one another. Here is a brief introduction and comparison of some of the basic object types covered in this article:

  • NSPersistentContainer vs CKContainer

    NSPersistentContainer handles the ManagedObjectModel, The persistence coordinator (NSPersistentStoreCoordinator) and managed object context (NSManagedObjectContext) creation and management. Developers create instances of it through code.

    CKContainer, on the other hand, is similar to the sandbox logic of an application, in which you can store various resources such as structured data and files. Each application that uses CloudKit should have its own CKContainer (one application can be configured to serve multiple ckContainers, and one CKContainer can serve multiple applications). Developers usually do not create a new CKConttainer directly in their code, but usually through the iCoud console or in the Signing&Capabilities of the Xcode Target.

  • NSPersistentStore vs CKDatabase/CkRecordZone

    NSPersistentStore is an abstract base class for all persistent Core Data stores. It supports four persistent types (SQLite, Binary, XML, and in-memory). In a NSPersistentContainer, through declare multiple NSPersistentStoreDescription, can hold multiple NSPersistentStore instance (can be different types). NSPersistentStore does not have the concept of user authentication, but can be set to read-only or read-write mode. Because Core Data with CloudKit requires support for persistent history tracing, you can only synchronize NSPersistentStore with SQLite as the storage type. On the device, an instance of this NSPersistentStore will point to an SQLite database file.

    On CloudKit, there is only one type of structured data storage, but two dimensions are used to differentiate data.

    From the perspective of user authentication, CKDatabase provides three types of databases: private database, public database, and shared database. Users of the app (who are already logged in to an iCloud account) can only access their own private database, which is stored in their personal iCloud space and cannot be accessed by anyone else. The data stored in the public database can be invoked by any authorized app, which can read the contents even if the user is not logged into an iCloud account. Users of the application can share part of the data with other users of the same app. The shared data will be placed in the shared database, and the sharer can set the read and write permission of other users for the data.

    Data is also not placed together in a piecemeter manner in CKDatabase, they are placed in a specified RecoreZone. You can create as many zones as you want in each of the three databases. When CKContainer is created, a CKRecoreZone named _defaultZone is generated in each database by default.

    Therefore, when we save data to the CloudKit database, we need to specify not only the database type (private, public, and shared), but also the specific zoneID (no need to mark it when saving to _defaultZone).

  • NSManagedObjectModel vs Schema

    The NS-managed ObjectModel is a managed object model that identifies the Enities that correspond to Core Data. In most cases, developers use Xcode’s Data Model Editor to define them, and the definitions are stored in the XCDatamodeled file, which contains entity attributes, relationships, indexes, constraints, validations, configurations, and so on.

    When CloudKit is enabled in the application, a Schema is created in CKContainer. A Schema contains Record types, possible relationships between Record types, indexes, and user permissions.

    In addition to creating the contents of the Schema directly in the iCloud console, we can also create CKRecord in our code and have CloudKit automatically create or update the corresponding contents in the Schema for us.

    Schema has Security Roles, which can set different read and write permissions for World, icloud, and Creator.

  • Entities vs Record Types

    Although we usually emphasize that Core Data is not a database, entities are very similar to tables in a database. We describe an object in an entity, including its name, properties, and relationships. Finally, it’s described as NSEntityDescription and summarized into the NS-managed ObjectModel.

    Record Types is used in CloudKit to describe the names and properties of data objects.

    There is a large amount of information that can be configured in Enitiy, but Record Types correspond to only a portion of the description. Since there is no one-to-one correspondence between the two parties, it is important to follow the rules (which will be discussed in the next article) when designing Data objects for Core Data with CloudKit.

  • Managed Object vs CKRecord

    Managed Objects are model objects that represent persistent records. A managed object is an instance of an NS-managed object or a subclass of it. Managed objects are registered in managed ObjectContext (NS-managed ObjectContext). In any given context, there is at most one instance of a managed object corresponding to a given record in persistent storage.

    On CloudKit, each record is called a CKRecord.

    We don’t need to care about creating the Managed ObjectID (NSMangedObjectID), Core Data will handle everything for us, but for CKRecord, most of the time we need to explicitly set CKRecordIdentifier for each record in our code. As the unique identifier of a CKRecord, CKRecordIdentifier is used to determine the unique location of the CKRecord in the database. If the data is stored in a custom CKRecordZone, we also need to specify this in ckRecord.id.

  • CKSubscription

    CloudKit is a cloud service that responds to data changes on different devices using the same iCloud account (private databases) or devices using different iCloud accounts (public databases).

    When data in CKContainer is changed, the cloud server checks whether the change meets the trigger of a CKContainer. When the change is met, the cloud server checks whether the change meets the trigger of a CKContainer. Remote notifications are sent to subscribed devices. This is why Xcode automatically adds the Remote Notification when we add the CloudKit feature in Xcode Target’s Signing&Capabilities.

    In practice, there are three subclasses of CKSubscription required to complete different subscription tasks:

    CKQuerySubscription: pushes Notification when a CKRecord satisfies the NSPercidate setting.

    CKDatabaseSubscription is used to subscribe to and track creation, changes and deletions recorded in the CKDatabase. This subscription can only be used for custom CKRecordZones in private and shared databases, and only the creator of the subscription is notified. In a future article, we’ll see how Core Data with CloudKit uses this subscription in private libraries.

    CKRecordZoneNotification, a subscription to a recordzone is executed when the user, or in some cases, CloudKit, modifiers a record in that zone (CKRecordZone), for example, when the value of a field in the record changes.

    For remote notifications pushed by the iCloud server, the Application needs to respond in an Application Delegate. In most cases, Remote notifications can be in the form of silent notifications. For this, developers need to enable background Modes of Remote Notifications in their applications.

Core Data with CloudKit implementation guess

With that in mind, let’s try to guess what Core Data with CloudKit might look like.

Take private database synchronization as an example:

  • Initialization:

    1. createCKContainer
    2. According to theNSManagedObjectModelconfigurationSchema
    3. Create a private database with ID ascom.apple.coredata.cloudkit.zonetheCKRecordZone
    4. Create on a private databaseCKDatabaseSubscription
  • Data Export (export local Core Data to the cloud)

    1. NSPersistentCloudKitContainerCreate background task responsePersist history tracingtheNSPersistentStoreRemoteChangenotice
    2. According to theNSPersistentStoreRemoteChangethetransactionThat will beCore DataToCloudKitIn the operation. For example, for new data, theNSManagedObjectInstance conversion toCKRecordInstance.
    3. throughCloudKitWill be convertedCKRecordOr otherCloudKit operationPassed to theiCloudThe server
  • The server side

    1. The submissions from the remote device are processed sequentiallyCloudKit Operation data
    2. Created from initializationCKDatabaseSubscriptionCheck whether this operation results in a private databasecom.apple.coredata.cloudkit.zoneChanges the data in
    3. For all creationCKDatabaseSubscriptionSubscribed devices (sameiCloudAccount) to distribute remote notifications
  • Data import (synchronizing remote data to local)

    1. NSPersistentCloudKitContainerThe background task created responds to the cloud taskSilent push
    2. Send the refresh operation request to the cloud with the last operationThe token
    3. Cloud based on each deviceThe tokenTo return changes to the database since the last refresh
    4. Converting remote data to local data (delete, update, add, etc.)
    5. Due to theView contexttheautomaticallyMergesChangesFromParenProperty is set to true, local data changes will automatically occur inView contextCome out in

All technical difficulties and details are omitted in the above steps and only the general process is described.

conclusion

In this article, we’ve covered a few basics about Core Data, CloudKit, and Core Data with CloudKit. In the next article, we’ll look at how to synchronize your local and private databases using Core Data with CloudKit.

PS: There are many articles on how to use NSPersistentContainer, but like other Core Data features, it is not easy to use. In more than two years of use, I encountered a number of problems. Taking the opportunity to implement the function of shared database in health Note 3 this year, I recently systematically relearned Core Data with CloudKit and combed through its knowledge points. I hope this series of posts will help more developers understand and use Core Data with Cloudkit.

This article originally appeared on my personal blog, Swift Notepad.