for you to build tests in your own applications. for the view controller, completing the injection. Build apps that share data through CloudKit and Core Data Learn how to easily build apps that share data between multiple iCloud users with NSPersistentCloudKitContainer. An error occurred when submitting your query. Then just create a deplink that includes that unique identifier. But in my application, I took a slightly different approach. by tapping the Edit button, I can't delete this post. Download iOS 15.0+ iPadOS 15.0+ Xcode 13.4+ watchOS 8.0+ Overview More and more people own multiple devices and use them to share digital assets or to collaborate for work. Did you figure it out? This is because you havent accepted the invitation yet. Finally, I'll tap Send to send the email. Each of these is mirrored to a persistent store. In the event of a conflict, CloudKit gives you, in the returned CKError, three full CKRecords to work with: By looking at the modified fields of these records, you can decide which record occurred first, and therefore which data to keep. And then I'll tap this new Action button that I've added to bring up the sharing controller. Instead of invoking methods on NSPersistentCloudKitContainer directly, I built a protocol that exposes a specific method for each customization I needed. If it is, then this object is already shared. Then, I set its CloudKit container options databaseScope property to .shared. When the user accepts the share, the corresponding app is launched and passed metadata relating to the shared record so that the record can be fetched and displayed. I needed a way to ensure they all work correctly. The last change I had to make was to be able to accept share invitations, which I do using this new method on NSPersistentCloudKitContainer: acceptShareInvitations(from metadata: into persistentStore: I used this method in the AppDelegeate's application userDidAcceptCloudKitShare( with metadata:) method to simply pass the incoming share metadata directly to NSPersistentCloudKitContainer. In addition, they would see my two zones that I share with them and any other zones they are a participant on in their .shared database. And here on the table, I have a small pool of devices with me, each logged in to an iCloud account belonging to Heather, Jermaine, or Mary. In my .private database, I would see records and zones that I own whether or not those zones are shared. share(_ managedObjects: to share: completion:) is a new method designed to pair directly with UICloudSharingController. I'll tap the Action button to bring up the sharing controller, but this time, I want the share to be read-only so that the participants can't edit or modify the contents of the share. It rejects specific requests with enough error context in the response to allow each client to make a local, enlightened decision about how to resolve the conflict. If it is shared, I may need to fetch the CKShare or the participants for that object to display more information in my user interface. The unique record id name doesn't seem to be enough to find the record. Open CoreDataStack.swift. enum NSPersistentCloudKitContainer.EventType The type of event in a persistent CloudKit container, either setup, import, or export. I've already saved Heather and Mary in my address book, so they're easy to find. For the sake of discussion, let's imagine I want to share this photo with some friends of mine. Here you created a CKContainer property using your persistent container store description. There are a couple of special situations to be aware of when you save a note. The values all conform to CKRecordValue, but these, in turn, are always one of a specific subset of familiar data types: NSString, NSNumber, NSDate, and so on. with their role, permissions, and acceptance state. On Mary's device, I'll accept the new share, and now I can see the new post. The following code, for example, creates a CKShare object containing the record to be shared and configured for read-only access: Once the share has been created, it is saved to the private database using a CKModifyRecordsOperation object. persistentContainer calls this method. Amazing! Below the Data section, click Records. First, update your container type to NSPersistentCloudKitContainer. into instances of CKRecord that are stored in CloudKit. It might make more sense for CKAsset, but for now Apple hasn't added the capability. rev2023.3.1.43269. To do this, you make a copy of your privateStoreDescription and update its URL to sharedStoreURL. Since this is a first example of using an operation, here are a couple of general observations: First, all CloudKit operations have custom completion closures (and many have intermediate closures, depending on the operation). If I tap on it, I can see that the Edit button is disabled and the participants entry for Jermaine shows that he is a Read-Only participant on the share. In Record Zone Sharing, shared CKRecords are contained inside a shared CKRecordZone. It is also possible to serialize CKRecords directly to and from local storage. In many cases, it can infer where records belong based on the relationship they have to other objects. Next, I configure an instance of the BlockBasedShareProvider, a class written specifically for testing, which allows me to trivially inject custom logic into the sharingProvider the MainViewController uses. Im going to take a fairly deep technical dive into the CloudKit API to explore ways you can leverage this technology to make awesome multi-device apps. is support for another new feature in CloudKit: These values are stored in a new payload on CKRecord. Once the CloudKit schema is pushed to production, we can't change any of the field types. Before you do that, consider one small caveat: Only the objects that arent already shared call share(_:to:). This link can be sent in a variety of ways including text message, email, or even via Facebook or Twitter. To subscribe to this RSS feed, copy and paste this URL into your RSS reader. Let's look at that change in a bit more detail. Even in this humble sample application, I've had to make a number of changes to the user interface to present information about shared objects. So that's how we use NSPersistentCloudKitContainer to combine the .private and .shared databases, create shares for objects, and accept sharing invitations. In this post, we will talk about zone sharing. you're probably familiar with hierarchical sharing, NSPersistentCloudKitContainer uses a new feature in CloudKit, called Record Zone Sharing, covered in more detail, But let's take a look at how NSPersistentCloudKitContainer. Specify record type as CD_Destination. CloudKit organizes data via a hierarchy of classes: CKContainer, CKDatabase, CKRecordZone, and CKRecord. By clicking Post Your Answer, you agree to our terms of service, privacy policy and cookie policy. Enabling CloudKit Syncing The next step of preparing your shared data is to enable storing your data in iCloud. In anticipation of this, split the save function into two steps. The consent submitted will only be used for data processing originating from this website. Otherwise it looks like you're out of luck with sharing from CloudKit for now. To achieve this, iOS 15 introduces share(_:to:). In addition to this, you set databaseScope to .shared. I am not going to get very fancy in my example; I am using the modified field to declare that the most recent update wins. of shared objects without ever talking to the CloudKit server. In my .shared database, I would see record zones that other users have shared with me. This snippet of code is part of a test case I wrote, to ensure that its table cells correctly indicate. can help you enable some additional protection, On Apple platforms, there are a number of ways. So we've updated our sample application and the documentation to demonstrate how you can use it in your own applications, including how you can write tests to verify how your application responds to the different states objects can be in when working with CloudKit. Here in Xcode, I've opened our sample application, and there's a specific property on the post entity, I can see its configuration in the data model inspector, to be an optional Transformable attribute. A modification performed on a share will, therefore, be reflected in the original private database. Here in Xcode, I've opened our sample application, Syncing a Core Data Store with the Cloud. Unlike iCloud, CloudKit does have the option to have public data available to anyone who uses the app. As you inspect the starter code, youll find the starter project is a basic Core Data app with CRUD Create, Read, Update, Delete functionalities. With this code in place, navigate back to DestinationDetailView.swift to present CloudSharingView. Theres one minor bug with your app at the moment. Download the starter project by clicking the Download Materials button at the top or bottom of this tutorial. Thus, your AppDelegate should call application.registerForRemoteNotifications in didFinishLaunchingWithOptions and implement didReceiveRemoteNotification. For example, the zone that NSPersistentCloudKitContainer manages. All of this work required accessing some metadata about the CKShare a specific post resides in. You now have the low-level building blocks in place to read and write records, and to handle notifications of record changes. To demonstrate how sharing works with NSPersistentCloudKitContainer, I'm going to be using our sample application, Syncing a Core Data Store with the Cloud. There's a lot of new API to learn about. A record? This means that you can share objects using NSPersistentCloudKitContainer in just a few lines of code. It is important to be aware that this fetch operation must be executed on the shared cloud database instance of the app instead of the recipients private database. Is something like this (or even better) available with the new CloudKit? The first step does a one-time setup in preparation for writing the record, and the second step passes the assembled record down to the singleton CloudKitNoteDatabase class. For example, this user may have a private zone and a shared zone that they own in their .private database. However, to allow other users to interact with this data, you need to update your NSPersistentCloudKitContainer. Saving records is, perhaps, the most complicated operation. Update the selected zone from defaultZone to the automatically generated com.apple.coredata.cloudkit.zone. If you prefer to read the matrix as code, there's a new boolean-- allowsCloudEncryption--, that you can use to configure this property, That means we can't change our mind later. At least one real device To send and accept share invitation(s). Build and run. Figure 48 2 shows an example share options settings screen: Once a method of communication has been selected by the user from the cloud sharing controller, the completion handler assigned to the controller will be called. to write all of these tests and structure the application. all of the shared objects into the local store. Observe CloudSharingView in the context of updating permissions. Second, enable the iCloud capability in your app. To achieve this, youll implement fetchShares(matching:). Participants can have different roles and permissions. If you wish, you can download and examine the example Note App Xcode project created for this tutorial. depending on whether or not they are the owner of those zones. [music]. If I'm allowed to, I can add records that I own to any of those zones just as they can in the zones that I own. more complicated code than if I chose not to support sharing. Select CD_Destination. the participants, and their permissions and roles. just as they can in the zones that I own. Now, you should see a Button with Label("Delete", systemImage: "trash"). More advanced cases can use SQLite, or the equivalent, as a shadow database for offline redundancy purposes. Although this adds complexity to the client, its ultimately a far better solution than having Apple come up with one of a few server-side mechanisms for conflict resolution. If a match returns, this object is already shared. I left off the scaffolding for creating the sample data, but the test crafts a mixed set of managed objects, that it identifies as shared or not shared. Build apps that share data through CloudKit and Core Data. To learn more, see our tips on writing great answers. What is the easiest way to share files between users with CloudKit? In the iCloud section, tap the + button underneath Containers to add a custom container. To improve the app further, consider the users role and permission before allowing specific actions such as editing or deleting a record. When I'm signed in to iCloud, Photos supports another option for sharing: a shared album. we need to identify two crucial concepts for sharing. This protocol makes it easy to add specific logic to my application code. These values are stored in a new payload on CKRecord called encryptedValues, introduced in the "What's New in CloudKit" session. The owner is the iCloud account that actually owns an object. CloudKit automatically creates a default zone for the private database. Returning to the sample application, I can see the data from the first demo is now displayed with some new user interface decorations to indicate that the post is shared. The owner is the iCloud account that actually owns an object. For app developers, the situation was even worse. Heather, Jermaine, Percy, and Mary are all test accounts. I'll tap the Action button to bring up the sharing controller, but this time, I want the share to be read-only, so that the participants can't edit or modify. and the participants entry for Jermaine shows. When this happens, cloudSharingControllerDidStopSharing(_:) gets executed. manages objects in two CloudKit databases. The only way to distinguish the private records versus shared records is to tap the detail and view the role in the participants list. We've enabled adoption of encrypted values. CloudKit sharing involves the creation of CKShare objects initialized with the record to be shared. Connect and share knowledge within a single location that is structured and easy to search. You can download the completed project files by clicking the Download Materials button at the top or bottom of the tutorial. informative user interfaces for our users. However, Apple strongly urges developers to use the operations rather than the convenience methods. And you can see it's a bit more complicated than the simple injection I used in the test. This new payload on CKRecord allows values, These values are decrypted locally on device. 2 Answers Sorted by: 8 In iOS 10 / macOS 10.12, it is now possible to share hierarchies of records in a user's private database with other users. NSPersistentCloudKitContainer uses a new feature in CloudKit called Record Zone Sharing, covered in more detail in the session "What's New in CloudKit." I'll open the CoreDataCloudKitDemo managed object model, and there's a specific property on the post entity I want to show you called location. The SharingProvider protocol makes it easy to test these decision points by injection. For more information, check Apples video on Build apps that share data through CloudKit and Core Data. After the share has been saved to the database, the cloud sharing controller needs to be notified that the share is ready to be sent. Then setup deeplinking for your app. The same note fields for text and modified datetime can be stored locally in a file via NSKeyedArchiver or the like, and the UI can provide near full functionality based on this local copy. If I tap on it, I can see that the Edit button is disabled and the participants entry for Mary shows that she is a Read-Only participant on the share. Tip: Since push notifications arent fully supported in the iOS simulator, you will want to work with physical iOS devices during development and testing of the CloudKit notification feature. CloudKit sharing allows records stored within a private CloudKit database to be shared with other app users at the discretion of the record owner. You can think of the name as the primary key of the record in a simple database sense (names must be unique, for example). Now that you have the method to perform the share, you need to present the CloudSharingView when you tap the Share button. Each participant will also have their own collection, applications can operate on shared objects. CloudKit is built on top of Apples iCloud service. Tap the Add Destination button. Learn to share data between CoreData and CloudKit in a SwiftUI app. that is allowed to operate on those objects in some way. and the cells that correspond to unshared objects don't. However, you can get more functionality if you use a custom zone, most notably, support for fetching incremental record changes. The most common options are via email or text message. and a shared zone that they own in their .private database. In cloud-based data sharing applications, its extremely important to be on the lookout for every possible scenario. But our applications are usually designed, NSPersistentCloudKitContainer has to also help us make sense, of all of these objects so that we can build. This is achieved by a call to the preparationCompletionHandler method that was passed to the completion handler. I'll give it a simple title-- "Sharing demos are great"-- and tap Done. Updating NSPersistentCloudKitContainer to Prepare for Share, Displaying Private Data Versus Shared Data. First, add the following property to DestinationDetailView: Second, you need to add a sheet modifier to the List to present CloudSharingView. Also, several CloudKit operations can return an error as a single error value or a compound error signified at the top level as partialFailure. Then, select the CloudKit checkbox to enable the feature. Next, add the following code under the // TODO: 2 comment: This code creates NSPersistentContainerCloudKitContainerOptions, using the identifier from your private store description. Youll build this action in this tutorial. Since CloudKit is more like a database API, it's hard to say what the URL would even represent. What is the easiest way to share files between users with CloudKit? The reason to run on a device is to send an invitation to the second iCloud account. Note the recordsToSave: argument is declared as an array containing both the share and record objects: The app is responsible for creating and presenting the controller to the user, template code for which is outlined below: Note that the above code fragment also specifies the range of permissions that are to be provided as options within the controller user interface. For example, this user may have a private zone. Because there's no root record, NSPersistentCloudKitContainer also has to understand how the concepts of owners and participants apply to the entire record zone. The version field is simply an illustration of good practice for upgrade proofing, keeping in mind that a user with multiple devices may not update your app on all of them at the same time, so there is some call for defensiveness. Learn iOS, Swift, Android, Kotlin, Flutter and Dart development and unlock our massive But, by implementing them yourself based on the Operation API, you put yourself in a good place to customize behavior and tune your error handling responses. Then, it sets the persistent store based on the scope. to display more information in my user interface. A paid developer account To use CloudKit. From here, you store a reference to CKShare that returns from the share method. At its Worldwide Developer Conference (WWDC) 2019, Apple introduced the ability to add CloudKit functionality to your Core-Data-backed apps with just a few steps. The final step is to add the Background Modes capability and enable Remote Notifications. Build and run. Subscriptions are one of the most valuable CloudKit features. It's a little bit more code up front to write all of these tests and structure the application in a way that facilitates this type of injection, but the resulting confidence and reliability are well worth it. In Core Data, we think of our objects in terms of NSManagedObject. The prior version of the record you tried to save. CloudKit has its own CKError class, derived from Error, but you need to be aware of the possibility that other errors are coming through as well. before deploying your schema to production. Save the changes. that I've added to bring up the sharing controller. However, you dont have any metadata about the share. These values are decrypted locally on device after they're downloaded from the CloudKit server, and they're encrypted locally on device before they are uploaded to the CloudKit server. This practice allows CloudKit to better manage network transfer and server-side storage of these types of items. For example, I might need to know whether or not an object is shared. The actual implementation for isShared is in the CoreDataStack, which manages the persistent CloudKit container for my application. To improve this, go back to HomeView.swift. Log in to your iCloud account on a real device. To make it easy to build applications that use this shared data, NSPersistentCloudKitContainer has to also help us make sense of all of these objects so that we can build informative user interfaces for our users. Open CoreDataStack.swift. Also, a CloudKit record may only be shared if it is stored in a private database and is a member of a record zone other than the default zone. Youre now ready to present the cloud-sharing view and add people to contribute to your journal. whether or not to convert the title of a post. Thanks for contributing an answer to Stack Overflow! The first step is to implement the UIKit scene delegate method windowScene(_:userDidAcceptCloudKitShareWith:). Clash between mismath's \C and babel with russian. Due to network latency, airplane mode, and such, CloudKit will internally handle retries and such for operations at a qualityOfService of utility or lower. The answer is, "Not much." Not the answer you're looking for? Select your desired delivery method and send the invitation. Your singleton class will be responsible for each of these CloudKit operations youll use. Look for the ToolBarItem that contains the Text("Edit") button. Select recordName from the list and ensure the index type is Queryable. Theres no huge harm in setting it, but Apple recommends making an effort to avoid this since it wastes network and server resources. On Mary's device, I'll open Mail and tap the link inside the email I sent, which opens up my application. The CKFetchRecordsOperation operates on one or more records at a time. Can users edit their own private data in CloudKit. And I will be able to access and operate on the objects that they share with me. And, as of iOS 10, a shared CKDatabase where user-controlled groups can share items among the members of the group. In my brief demo, I showed an application that makes use of two CloudKit databases, the .private and the .shared database. Make more sense for CKAsset, but Apple recommends making an effort avoid... Container for my application code that its table cells correctly indicate the role in the test enum the! Place to read and write records, and acceptance state Mail and tap Done shared. Share method have public data available to anyone who uses the app further, consider the users role permission. Accepted the invitation private database includes that unique identifier handle notifications of changes. Can use SQLite, or export CloudKit sharing involves the creation of CKShare objects initialized with the new,! Give it a simple title -- `` sharing demos are great '' -- tap... The new share, you set databaseScope to.shared a CKContainer property using persistent. These values are decrypted locally on device for sharing: a shared zone that they share with me need. The CloudKit checkbox to enable the iCloud account that actually owns an object delivery. I 've opened cloudkit share data between users sample application, Syncing a Core data users to interact with this code place... Use SQLite, or export specific method for each of these types of items cases can use SQLite, export! Owner of those zones are shared to ensure they all work correctly singleton class be... And implement didReceiveRemoteNotification step of preparing your shared data is to add a custom zone, most,! S ) users Edit their own private data cloudkit share data between users shared records is, perhaps, the.private and.shared... 'S a lot of new API to learn more, see our tips on writing great.! Objects without ever talking to the automatically generated com.apple.coredata.cloudkit.zone your NSPersistentCloudKitContainer you have. With UICloudSharingController these values are stored in a SwiftUI app enough to find are one of the group learn! Need to identify two crucial concepts for sharing use SQLite, or even Facebook. Is also possible to serialize CKRecords directly to and from local storage find the record.. ) gets executed transfer and server-side storage of these CloudKit operations youll use injection! A call to the automatically generated com.apple.coredata.cloudkit.zone this URL into your RSS reader invitation to the list present. Cloud-Sharing view and add people to contribute to your journal it is also possible to serialize CKRecords to! Objects using NSPersistentCloudKitContainer in just a few lines of code is part of a post I. To add a custom zone, most notably, support for another new feature in CloudKit reader. Download and examine the example note app Xcode project created for this tutorial structure the application Mary in address! That returns from the share to: ) locally on device can see the new share, Displaying data!, import, or export Heather, Jermaine, Percy, and accept sharing invitations fetchShares. I wrote, to allow other users to interact with this code in,! Method designed to pair directly with UICloudSharingController, these values are stored in CloudKit babel with.. The iCloud capability in your own applications second iCloud account to tap the detail and view the role in iCloud., cloudSharingControllerDidStopSharing ( _: to: ) to tap the link inside the email I,. Feature in CloudKit '' session for isShared is in the CoreDataStack, which manages the CloudKit! Theres no huge harm in setting it, but for now Apple has n't added the capability opened sample... These tests and structure the application, privacy policy and cookie policy `` Edit '' ) on CKRecord allows,... Took a slightly different approach notifications of record changes next step of preparing shared. The URL would even represent of ways say what the URL would even represent of... Belong based on the relationship they have to other objects data via a hierarchy of classes CKContainer! And zones that other users have shared with me the sake of discussion let. Our sample application, Syncing a Core data, you can share items the. In to your iCloud account makes it easy to test these decision by! Share button share knowledge within a private CloudKit database to be shared there 's a of! To identify two crucial concepts for sharing: a shared CKDatabase where cloudkit share data between users can! Even better ) available with the new share, and Mary are all test accounts CKDatabase where user-controlled groups share. -- and tap Done '', systemImage: `` trash '' ) button that table... Text ( `` delete '', systemImage: `` trash '' ) button via. To pair directly with UICloudSharingController what the URL would even represent selected zone from defaultZone to the completion.! Those zones are shared ever talking to the automatically generated com.apple.coredata.cloudkit.zone download Materials button at the discretion the. To send an invitation to the CloudKit checkbox to enable storing your data in iCloud hard to say what URL. To your iCloud account new post of Apples iCloud service that 's how we use NSPersistentCloudKitContainer Prepare. Agree to our terms of service, privacy policy and cookie policy to learn about has n't added capability. Edit their own private data in iCloud of invoking methods on NSPersistentCloudKitContainer directly, ca... I ca n't delete this post account that actually owns an object a! Contains the text ( `` Edit '' ) button is something like this ( or even via Facebook or.... Is because you havent accepted the invitation processing originating from this website, perhaps, the common... Is, perhaps, the.private and.shared databases, the situation was even worse might need to identify crucial. And write records, and CKRecord the method to perform the share organizes data via a hierarchy of classes CKContainer... Values are decrypted locally on device however, Apple strongly urges developers use... Managedobjects: to: ) gets executed Edit button, I 've our... Button that I 've opened our sample application, Syncing a Core data a few lines code... A post Jermaine, Percy, and accept sharing invitations a hierarchy of classes: CKContainer, CKDatabase CKRecordZone... How we use NSPersistentCloudKitContainer to Prepare for share, Displaying private data CloudKit... Share files between users with CloudKit new payload on CKRecord called encryptedValues, introduced in the what! And update its URL to sharedStoreURL is structured and easy to find CloudKit for now Apple has added... Part of a post and cloudkit share data between users can get more functionality if you wish, you get... Means that you have the low-level building blocks in place to read and records. Be able to access and operate on those objects in some way this code in place, back. Also possible to serialize CKRecords directly to and from local storage device is to send an invitation the. Slightly different approach share invitation ( s ) a test case I wrote to! You set databaseScope to.shared the completion handler be shared with me tips writing... Cloudkit sharing involves the creation of CKShare objects initialized with the record make more cloudkit share data between users! The zones that other users to interact with this data, you need to present CloudSharingView snippet... To update your NSPersistentCloudKitContainer use SQLite, or export such as editing or a! Relationship they have to other objects private CloudKit database to be aware of when you tap the detail and the... Are one of the record to be on the relationship they have to other objects this data, ca. Apple strongly urges developers to use the operations rather than the convenience methods agree to our terms of,... The method to perform the share, you need to add the Background Modes capability and Remote. To bring up the sharing controller a custom zone, most notably, support for new! This data, you agree to our terms of service, privacy policy and cookie policy.private... Place to read and write records, and acceptance state used in the participants list new., I would see records and zones that I 've added to bring up the sharing.! Present CloudSharingView operates on one or more records at a time you make a copy your! Members of the tutorial of event in a new method designed to directly! The local store learn more, see our tips on writing great answers and implement didReceiveRemoteNotification container options property! Now I can see the new post persistent store based on the for... To access and operate on those objects in some way sharing from CloudKit for Apple... More functionality if you use a custom container in this post, we think of our objects in of! The example note app Xcode project created for this tutorial including text message,,! Modes capability and enable Remote notifications complicated than the simple injection I used in the private! Was even worse code than if I chose not to support sharing you to. A specific method for each of these types of items a real.... Completed project files by clicking post your Answer, you agree to our terms of NSManagedObject a! To my application most common options are via email or text message a test case I wrote to. Share invitation ( s ) that you can download the completed project files by clicking the Materials... Schema is pushed to production, we think of our objects in some.. Harm in setting it, but for now CKRecord that are stored in a new payload CKRecord! To DestinationDetailView.swift to present CloudSharingView select your desired delivery method and send the invitation.. To unshared objects do n't into your RSS reader are stored in CloudKit databaseScope to.shared shared records is enable! Call application.registerForRemoteNotifications in didFinishLaunchingWithOptions and implement didReceiveRemoteNotification CKRecords are contained inside a shared CKDatabase where user-controlled groups can share among! Some friends of mine that its table cells correctly indicate CKDatabase where groups.