Build apps that share data through CloudKit and Core Data

RSS for tag

Discuss the WWDC21 session Build apps that share data through CloudKit and Core Data.

View Session

Posts under wwdc21-10015 tag

29 Posts
Sort by:
Post not yet marked as solved
1 Replies
212 Views
I'm finding this issue after making sure I create an entity with the correspondent attribute. This issue comes from a relation between 2 entities: GroupData and TopicData. Their relations are like shown in the screenshots. Then, I get the context from a function like the next one. Every time I need to use the context, I call this function. import CoreData class DataController: ObservableObject {     let container = NSPersistentContainer(name: "vocabul-R")     init() {         container.loadPersistentStores { description, error in                 if let error = error {                     print("CoreData failed to load: \(error.localizedDescription)")                 }         }         container.viewContext.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump         container.viewContext.retainsRegisteredObjects = true     } } I first create the objects from data I get from csv files. By debugging I've made sure that the objects created have the correspondent values for each attribute and the correct objects in relations.     guard let filepath = Bundle.main.path(forResource: data_file, ofType: "csv") else {         return     }     let dataController = DataController()     let context = dataController.container.viewContext     do {         try context.save()         }     catch {     }     var data = ""     do {         data = try String(contentsOfFile: filepath)     } catch {         return     }     var rows = data.components(separatedBy: "\n")     rows.remove(at: 0)     for row in rows {         let columns = row.components(separatedBy: ",")         let name = columns[0]         let activation_level = (columns[1] as NSString).integerValue         let activated: Bool = false         let last_opened_group: Int16 = 0         let current_level: Int16 = 0         let topic = TopicData(context: context)         topic.name = name         topic.activated = activated         topic.activationLevel = Int16(activation_level)         topic.last_opened_group = last_opened_group         topic.current_level = current_level         do {             try context.save()             }         catch {             print("Context not saved")         }     } } func parseCSV_groups (data_file: String, topic_name: String) {     guard let filepath = Bundle.main.path(forResource: data_file, ofType: "csv") else {         return     }     var data = ""     do {         data = try String(contentsOfFile: filepath)     } catch {         return     }     var rows = data.components(separatedBy: "\n")     rows.remove(at: 0)     let dataController = DataController()     let context = dataController.container.viewContext     let topic = TopicData.getTopicFromName(name: topic_name, context: context)     let train_list = Train_list(context: context)     let test_list = Test_list(context: context)     train_list.train_list = "train_list"     test_list.test_list = "test_list"     for row in rows {         let columns = row.components(separatedBy: ",")         let index_group = (columns[0] as NSString).integerValue         let mean_level = (columns[1] as NSString).integerValue         let cleared_words: Int16 = 0         let group = GroupData(context: context)         group.index_group = Int16(index_group)         group.mean_level = Int16(mean_level)         group.cleared_words = cleared_words         //group.topic = topic         topic.addToGroup(group)         do {             try context.save()             }         catch {             print("Context not saved")         }     } } I have created objects from both entities with relations. I have other functions with relations between these and other entities seem to work fine. But now I need to get all the groups that one TopicData object has. For that, I first get a topic from its name.         let fetchRequest = NSFetchRequest<TopicData>(entityName: "TopicData")         fetchRequest.predicate = NSPredicate(format: "name = %@", name)         let topics = (try? context.fetch(fetchRequest)) ?? []         let topic = topics.first ?? TopicData()         return topic     } This seems to work fine as I can print the topic name and the object type (which is TopicData). But then I try to get the groups that it has. I've also tried to get the groups with a fetchRequest     /fetchRequest.predicate = NSPredicate(format: "topic = %@", topic)     let groups = (try? context.fetch(fetchRequest)) ?? [] And when I try to get one of the attributes (activated) of one of these topics, I also get an error. I thought this could be happening because every time I use a topic to update it and save it on context again, a new topic is created which may not be related to GroupData. But I added a constraint in TopicData for name and I still get the same error. Another thing I thought is that relations could be disappearing, since I saw some people had this problem and the issue was that. But I added the retainsRegisteredObjects line in DataController and it didn't work. The full error I get is: Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[TopicData group]: unrecognized selector sent to instance 0x600003812bc0' terminating with uncaught exception of type NSException I think that is all about the problem. Any idea of what could be happening? I think I added everything relevant for the problem, but let me know if there is some other code I should add. Thank you!
Posted Last updated
.
Post not yet marked as solved
1 Replies
151 Views
I'm getting this error because it seems the database forgets objects relations. I always use the same context (with different instances on each function), so I think I can't do anything with mutableSetValue. Also, I've tried to set retainsRegisteredObjects, but it doesn't seem to improve anything. An option I was thinking about is always using the same context and send it from one function to the next one, but it doesn't feel like that's an optimal solution. And I'm not sure if relations would stay after I closed the app, which is something I need. Any ideas of how to solve this issue?
Posted Last updated
.
Post not yet marked as solved
0 Replies
119 Views
I’m new to trying to modify share records and not having any luck with it. Somehow, I’ve made it such that every new coredata record I create is already listed as shared and accepted by another user (my other sandbox). This user never got a link nor has the record. I’d like to correct that issue. But hacking away at it, I can’t find a solution or even know what caused this. I assume I need to modify the record of the privateCloudDatabase?
Posted
by Bill3D.
Last updated
.
Post not yet marked as solved
8 Replies
2.2k Views
I have been excited to add NSPersistentCloudKitContainer's share functionality to my app but I've noted a few thing I suspect are bugs: -When using share() on a NSManagedObject the share record is set up in a new zone. However, the root NSManagedObject's record is not being updated with the relationship linkage to the shared record. -Secondly, when you revoke a share, the cloudkit.share record is removed from iCloud, but not in the local data stores. This makes the fetchShares() method ineffective for detecting a missing cloudkit.share record. In order to re-share the root object the developer must call out to iCloud directly using the old methods to be sure if the share exists or not. I am using the code from Apple's 'Synchronizing a Local Store to the Cloud' sample. It would be nice if they added support for revoking shares into this sample and addressed these issues.
Posted
by stokaace.
Last updated
.
Post not yet marked as solved
0 Replies
415 Views
Hi, I wanted to offer the UI for editing and deleting a share. So for using UICloudSharingController(share: , container: ) I implemented the delegate to call "persistUpdatedShare" in "cloudSharingControllerDidSaveShare" func cloudSharingControllerDidSaveShare(_ csc: UICloudSharingController) { if let share = csc.share,   let privatePersistentStore = PersistenceController.shared.privatePersistentStore { PersistenceController.shared.container.persistUpdatedShare(share, in: privatePersistentStore) { (share, error) in Thread.performOnMain { if let error = error { PersistenceController.shared.storeWarning = error } } } } } and to call "purgeObjectsAndRecordsInZone" in "cloudSharingControllerDidStopSharing" func cloudSharingControllerDidStopSharing(_ csc: UICloudSharingController) { let puzzleToShare = puzzleToShare if let share = csc.share,   let privatePersistentStore = PersistenceController.shared.privatePersistentStore { PersistenceController.shared.container.purgeObjectsAndRecordsInZone(with: share.recordID.zoneID, in: privatePersistentStore) { (zoneID, error) in Thread.performOnMain { if let error = error { PersistenceController.shared.storeWarning = error } else { puzzleToShare.makePrivateDuplicate() try? puzzleToShare.puzzle.managedObjectContext?.save() } } } } } I got this implementation from a TSI, but it makes no sense to me to delete and duplicate my objects rather than just keeping them and only deleting the share. Question: How can I cleanly discontinue a share, but keep my data. Duplicating my objects seems such a weird approach. But without this implementation it does not really work. (I get strange behaviors and crashes) All the best Christoph
Posted Last updated
.
Post not yet marked as solved
5 Replies
1.1k Views
G'day everyone, I've had some problems with CoreData+CloudKit, and figured I would go back to basics and start from scratch with a new project. (Note: I'm using the Xcode 13.0 Beta with iOS 15 to try to use sharing on NSPersistentCloudKitContainer as discussed in WWDC2021, but these problems are scattered over the net without any answers to date) When I go through the motions of adding iCloud to the project, assign a new (unique) container and then build the app, two problems arise: the simulator comes back with a blank screen (without the Edit or add controls from the boilerplate). Even the preview pane seems broken (no "Add Item" button). This worked in Xcode 12, so I am assuming there is something that's come up as a bug here. More importantly, I get a swath of errors and warnings in the debug console related to CloudKit. It looks like there is some sort of identity problem here. There is no schema generated as yet in the CloudKit dashboard, so I am guessing that the migration errors are happening because the database hasn't yet established because of the identify problem. Does anyone have any insight into what I'm missing here? Thanks.
Posted
by Andy_J_D.
Last updated
.
Post marked as solved
1 Replies
214 Views
I have been stuck on this for some time now, I built a tableView App with the ability to delete a row. I am using with CoreData and iCloud. When I use "editActionsForRowAt" it deletes from the view and does not re-appear when I close and run the App again, the problem is as you know it is deprecated in iOS 13. 1.) This works great but is deprecated in iOS 13 override func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? { //Social Sharing Button let shareAction = UITableViewRowAction(style: UITableViewRowAction.Style.default, title: "share", handler: { (action, indexPath) -> Void in _ = "Just checking in at " + self.inspections[indexPath.row].stateID! }) // Delete Button let deleteAction = UITableViewRowAction(style: UITableViewRowAction.Style.default, title: "Delete",handler: { (action, indexPath) -> Void in if let appDelegate = (UIApplication.shared.delegate as? AppDelegate) { let context = appDelegate.persistentContainer.viewContext let inspectionsToDelete = self.fetchResultController.object(at: indexPath) context.delete(inspectionsToDelete) appDelegate.saveContext() } }) deleteAction.backgroundColor = UIColor(red: 237.0/255.0, green: 66.0/255.0, blue: 106.0/255.0, alpha: 1.0) shareAction.backgroundColor = UIColor(red: 63.0/255.0, green: 212.0/255.0, blue: 78.0/255.0, alpha: 1.0) return [deleteAction, shareAction] } 2.) So I ran with "trailingSwipeActionsConfigurationForRowAt" This should work but it will not swipe to delete the row. func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationsForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { let deleteAction = UIContextualAction(style: .normal, title: "Delete", handler: {(action, view, success) in if let appDelegate = (UIApplication.shared.delegate as? AppDelegate) { let context = appDelegate.persistentContainer.viewContext let inspectionsToDelete = self.fetchResultController.object(at: indexPath) context.delete(inspectionsToDelete) appDelegate.saveContext() } }) return UISwipeActionsConfiguration(actions: [deleteAction]) } 3.) This deletes without 1.) or 2.) but repopulates the row when I reopen the App override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { if editingStyle == .delete { // delete the row from the data source inspections.remove(at: indexPath.row) } tableView.deleteRows(at: [indexPath], with: .fade) }
Posted
by drewgost.
Last updated
.
Post not yet marked as solved
0 Replies
223 Views
I'm working on an app that uses NSPersistentCloudKitContainer to handle CloudKit sharing. Against all odds I've gotten the sharing to work, but now I'm seeing errors on startup that look very much like some kind of background loop trying to merge changes from multiple users and failing. In a more traditional CloudKit installation not backed on NSPersistentCloudKitContainer this feels like a case where I'd have to provide some code to handle the merge. In the brave new world I can't seem to find anyway to affect this Mirroring Delegate. It starts when I initialize the NSPersistentCloudKitContainer and produces the error below (as well as a long stream of similar errors). Any ideas? error: CoreData+CloudKit: -[NSCloudKitMirroringDelegate _exportFinishedWithResult:exporter:](1347): <PFCloudKitExporter: 0x282d2ead0>: Export failed with error: <CKError 0x280079470: "Partial Failure" (2/1011); "Failed to modify some records"; partial errors: { cloudkit.zoneshare:(Pacts:__defaultOwner__) = <CKError 0x280079680: "Server Record Changed" (14/1022); "Participants conflict while trying to update share from the server. Participants: === Client: (     "<CKShareParticipant: 0x100cebb70; participantID=..., isCurrentUser=true, role=owner, permission=readWrite, acceptanceStatus=Accepted, identity=<CKUserIdentity: 0x100c4cae0; userID=__defaultOwner__:(_defaultZone:__defaultOwner__), nameComponents=, cached=false, publicKeyVersion=2>, hasProtectionInfo=true, invitationTokenStatus=Healthy, isAnonymousInvitedParticipant=false>" ) === Server: (     "<CKShareParticipant: 0x100c26db0; participantID=..., isCurrentUser=true, role=owner, permission=readWrite, acceptanceStatus=Accepted, identity=<CKUserIdentity: 0x100c13b60; userID=__defaultOwner__:(_defaultZone:__defaultOwner__), nameComponents=..., lookupInfo=<CKUserIdentityLookupInfo: 0x100c13c00; email=...>, cached=false, publicKeyVersion=2>, hasProtectionInfo=true, invitationTokenStatus=Healthy, isAnonymousInvitedParticipant=false>",     "<CKShareParticipant: 0x100c25960; participantID=..., isCurrentUser=false, role=user, permission=readWrite, acceptanceStatus=Accepted, identity=<CKUserIdentity: 0x100c259f0; userID=_6cd4c7c8091c946d4b8e704efbfc0bc4:(_defaultZone:__defaultOwner__), nameComponents=..., lookupInfo=<CKUserIdentityLookupInfo: 0x100c68d10; email=...>, cached=false, publicKeyVersion=2>, hasProtectionInfo=true, invitationTokenStatus=Healthy, isAnonymousInvitedParticipant=false>" )"> }>
Posted
by powers.
Last updated
.
Post marked as solved
1 Replies
276 Views
Hi, I always though @FetchedResults should automatically update, but it seems this is not always true, or do I have to trigger updates myself? I am using Core Data with CloudKit sync. When I start my app, nothing is visually updating while it'S doing the first sync. Only after that view is reloaded it shows the synced data. Any idea what I have to do automatically get updates when the data is syncing the background. All the best Christoph
Posted Last updated
.
Post not yet marked as solved
0 Replies
180 Views
Hi, I'm building and when it start the app install in the simulator, this error happens Domain: NSPOSIXErrorDomain Code: 53 Failure Reason: Software caused connection abort -- System Information macOS Version 10.15.7 (Build 19H1519) Xcode 12.4 (17801) (Build 12D4e) Timestamp: 2022-02-24T20:05:09-03:00
Posted Last updated
.
Post not yet marked as solved
0 Replies
232 Views
have two quote dictionaries I'm trying to append after an in-app purchase. I have tried several different methods to append the dictionaries together but I'm still getting an error "No exact matches in call to instance method 'append'" I have established variables for each array to then append the array within the struct. Any thoughts? Is there a better method I should use to add the quotes from the array called QuoteDetails2 to the initial array QuoteDetails? Here is the code I'm trying to correct: var topQuotes = [QuoteDetails]() var additionalQuotes = [QuoteDetails2]() public struct QuoteProvider { static func all() -> [QuoteDetails] { return [ QuoteDetails( id: “1”, texts: “High school is fun”, authors: “SM” ), QuoteDetails( id: “2”, texts: “Middle School is fun”, authors: "A. Philip Randolph" ), QuoteDetails( id: “3”, texts: “The playground is fun”, authors: "Booker T. Washington" ), QuoteDetails( id: “4”, texts: "Hold on to your dreams of a better life and stay committed to striving to realize it.", authors: “KJ” ) ] } static func all2() -> [QuoteDetails2] { return [ QuoteDetails2( id: "1", texts: "The cat ran fast”, authors: " ME” ), QuoteDetails2( id: "2", texts: “The dog ran fast.”, authors: " ME” ), QuoteDetails2( id: "3", texts: "End life problems”, authors: “ME” ) ] } func showPremiumQuotes() { if UserDefaults.standard.bool(forKey: "premiumQuotes") == true { topQuotes.append(contentsOf: additionalQuotes) } } /// - Returns: A random item. static func random() -> QuoteDetails { let allQuotes = QuoteProvider.all() let randomIndex = Int.random(in: 0..<allQuotes.count) return allQuotes[randomIndex] } }
Posted Last updated
.
Post not yet marked as solved
1 Replies
358 Views
I am trying to add CloudKit sharing to my app using the new iOS 15 share method https://developer.apple.com/documentation/coredata/nspersistentcloudkitcontainer/3746834-share In the app there are 3 core data entities (Folder, Item, Comment) with the following relationships: A Folder contains many Items An Item has many Comments I want to use CloudKit to share just the Item entity, not any of its relationships. Is this possible to do with the share(_:to:completion:) method? Currently, when I pass an Item to the share method it also includes the Folder and Comments in the CKShare. How do I prevent this?
Posted
by ethan5513.
Last updated
.
Post not yet marked as solved
2 Replies
719 Views
Hey all, I've just taken Xcode 13 Beta 2, and I still can't get the sample code from the "Sharing-Data" code to work properly, and nor does the boilerplate code from creating a new project that incorporates CodeData and CloudKit work. Fo far my feedback items have not been identified as having other similar issues - based on the posts I'm seeing I'm not the only one experiencing the same problem. Has anyone got this code built and working yet? Mine breaks when you try to copy the sharing link.
Posted
by Andy_J_D.
Last updated
.
Post not yet marked as solved
0 Replies
306 Views
I am running the WWDC21 sample application "CoreDataCloudkitDemo" with two different Apple IDs to test the Cloudkit sharing feature. I use two simulators and one real device, where the latter has the same ID as one of the simulators. While creating invites via "Copy link" works on all three installations, accepting invites only works on the real device. Trying to open an invite (by copying the link in the one and pasting it into Safari in the other) always results in a web page stating that "iCloud has stopped responding" and "Unable to find applicationIdentifier for the containerId = [my ID] and fileExtension = undefined". Is this a simulator bug in Xcode 13.2 or is there anything I can do about it? Other iCloud features (including login via Safari to icloud.com) work in both simulators. Is there a way to open invite URLs in a simulator (as there is no functioning iMessage or Mail app) other than Safari's URL input field? Doing so does not work in the real device either, but clicking it in e.g. iMessage works. Edit: Pasting the link into a ToDo item and opening it from there works even in the simulator - so is it just a Safari issue?
Posted
by LPG.
Last updated
.
Post not yet marked as solved
2 Replies
735 Views
Dear: Use core data to store super long strings. After storage, the strings are incomplete. Xcode tips: CoreData: debug: PostSaveMaintenance: fileSize 30409752 greater than prune threshold CoreData: annotation: PostSaveMaintenance: wal_checkpoint(TRUNCATE)  help me?
Posted
by aiyowei.
Last updated
.
Post not yet marked as solved
0 Replies
375 Views
Now, when using NSPersistentCloudKitContainer.share(_ managedObjects: [NSManagedObject], to share: CKShare?), A deep traversal will be performed among the objects and any related objects will also be shared. For example, if there are 100 posts with the same tag, I shared one of them, and the remaining 99 will be shared at the same time. Is there any way to control whether the relationship should be shared?
Posted
by itengfei.
Last updated
.
Post not yet marked as solved
0 Replies
335 Views
Hello everyone, in the App I'm building I'd like to share an entire list of items (records) by just sharing the list itself (parent record or RecordZone). Meanwhile, I'd like to continue using the NSPersistentCloudKitContainer and therefore CoreData sync. Is it possible, with the new features Apple introduced (Sharing RecordZone and Sharing in Cloud & Local Storage), to do what I described, and combine both functions. Thank you, Carlos Steiner
Posted
by Carlos SN.
Last updated
.
Post not yet marked as solved
0 Replies
340 Views
I have an app for client management that stores data in Core Data with an NSPersistentCloudKitContainer. Each manager have their clients in the Core Data Private Database, and each client could have associated files (images, documents, etc) that are stored in app's own folder structure in iCloud Drive. Eventually, a manager can decide if to share a client with another manager, in order to have a shared managed client (readable, or writable+readable by others managers which share the same client). This is done by moving the Client from the Private Database to the Shared Database following the Apple's guidelines: https://developer.apple.com/videos/play/wwdc2021/10015/ This is the structure: The problem is: the database record is shared correctly, but the iCloud Drive Files are not shared (obviously). My goal is to get working the iCloud Drive Client Files (every client has a single Folder) with the minimum effort from the user. I cannot get working the iCloud Drive Sharing from my app, only the Core Data Sharing, so (in development environment) I have to share the Client Core Data Info from the App sheet, and then, go to Finder and share the Client's folder and send the link, so it would be a bit confusing for my users. Any approach for get working iCloud Drive File Sharing from the (inside) my app? Generate a link and automatically send to the other user. Or, better, an approach to get working this sharing with a single action from the user (only share once, and it sends the core data info and icloud drive file sharing).
Posted
by knshi.
Last updated
.
Post not yet marked as solved
3 Replies
807 Views
I've been struggling to get the CoreDataCloudKitDemo working. I've updated to Xcode 13 and followed the documented instructions closely. However, I'm getting this error: Provisioning profile "Mac Team Provisioning Profile: com.____.apple-samplecode.CoreDataCloudKitDemo" doesn't include the aps-environment entitlement. A bit of searching says this error is due to Push Notifications not being enabled. However, I do have them enabled. I cannot figure out why this won't run.
Posted
by stokaace.
Last updated
.