Post

Replies

Boosts

Views

Activity

Reply to CKRecord and UITableView
The table's dataSource is responsible for the table's data, so you could call the same code whether or not the CKRecord is nil, and when it's nil purge any existing data you had for the table, then refresh the table. Alternatively, that same code could hide/show the table and show/hide an alternate view (with a label that says "No records", for instance) when you have no records.
May ’22
Reply to Shared Records don't appear when app is in Production
When you say "it doesn't work", can you clarify what exactly you're doing, what you're expecting to see, and what you are seeing instead? When Alice and Bob are sharing a record and Alice changes something in that share, Bob will only see the change automatically if there's a subscription set up for it. If you are expecting a notification about the change, and you're seeing it work in Development but not in Production, verify that you have in fact established a subscription in Production. If, on the other hand, you are attempting to fetch a shared record with what should be a reliable recordID and this works in Development but not in Production, then it might be something else.
Jan ’22
Reply to Obtaining a share participant's avatar image
I've determined that the icons/avatars are indeed available, but through ContactsKit rather than CloudKit. The images shown are all images you have personally associated with the user's Contact entry in your Contacts app. How to retrieve the images: let share = mySharedRecord.share let participants = share.participants let userIdentities = participants.map {$0.userIdentity} let emails = userIdentities.compactMap {$0.lookupInfo?.emailAddress} let store = CNContactStore() let keysToFetch = [CNContactImageDataAvailableKey, CNContactImageDataKey, CNContactThumbnailImageDataKey] as [CNKeyDescriptor] emails.forEach { email in do { let predicate = CNContact.predicateForContacts(matchingEmailAddress: email) let contacts = try store.unifiedContacts(matching: predicate, keysToFetch: keysToFetch) let imageDatas: [Data] = contacts.compactMap {$0.imageData} let images = imageDatas.map {UIImage(data: $0)} return images } catch { // Handle the error return nil } }
Dec ’21
Reply to What should be the expected user experience from an app that uses CloudKit/Sync
The following is an opinion. Informed, but still an opinion. 1a. If your app's core functionality requires iCloud and the user is not signed into iCloud, display a clear message telling the user that they'll need to sign in to iCloud to use your app and include a button to take them to the system page for signing in. Do not simply display an alert, but rather show it in an attractive, polite, and modeless/full-window presentation. 1b. If your app includes a non-essential feature that requires iCloud and the user is not signed into iCloud, display a modeless notification/affordance to indicate that the feature is inactive and will remain so until they sign into iCloud. The notification might take the form of a special icon positioned where that feature is normally accessed. Tapping that icon (or hovering, on a Mac) might pop up a brief explainer. If the feature's importance warrants it, a more prominent notification might be used. Modeless is better than modal. Sync should be automatic. Users will expect this if they use your app across multiple devices. Making it automatic may also clarify what you choose to sync. User data should certainly sync, but user settings may not. If I set my background color to blue to match my blue iPhone, I might not appreciate that my silver iPad's background color is now blue as well. Resist the temptation to ask the user whether they'd like to sync this or that. Rather, make that determination yourself after carefully considering the value to the user. Also, if possible give the user a way to initiate a manual sync—it's reassuring—and display an indicator like a green checkmark to indicate that the sync completed successfully or failed. One common way to provide a manual sync is by including a pull-to-refresh on the scrollview containing the user's content.
Dec ’21
Reply to What is "shared PCS data"?
This turned out to be my fault. While it's true that I was refreshing the parent record before saving the child record, I was doing so in parallel with a prior save of another child record of the same parent, and the earlier save was completing before the later refresh. Swift: recordA.save() // recordA.parent == parent 1 recordB.save() // recordB.parent == parent 1 Here's what happens in those two calls. lines 1 and 4 were executed synchronously, then respectively initiating the async calls on lines 2 and 5, which in turn initiated the async calls on lines 3 and 6. The problem was that line 5 was completing before line 3, but line 3 was completing before line 6, so line 6 was failing because parent 1 had changed in the meantime (by line 3). 1: Save record A with parent 1 2: refresh parent 1 3: save record A 4: Save record B with parent 1 5: refresh parent 1 6: save record B I was naive to initiate lines 1 and 3 together and fortunate that they failed quickly, since they might have worked well—until they didn't. There are two solutions I can think of here: Batch records A and B together in a single operation. Chain A > B so that B is initiated only after A completes. The former solution is by far the preferable one, but the latter solution made more sense in my case, so the revised approach (which now works fine) looks like this: In Swift, it looks much friendlier: recordA.save() { recordB.save() { } 1: Save record A with parent 1 2: refresh parent 1 3: save record A 4: Save record B with parent 1 5: refresh parent 1 6: save record B I still don't know what "chain PCS data" means, but in essence it seems to indicate that the records you're attempting to modify have changed and thus cannot be modified. The solution is to get a fresh copy of those records and try again. In my case, it meant get a fresh copy of the parent before saving record B.
Dec ’21
Reply to New Database
When you configure your app to use CloudKit, 3 databases are created for you automatically: public, private, and shared. You don't create them yourself. You'll need to read the documentation for a full understanding, but a good place to start is https://developer.apple.com/documentation/cloudkit/designing_and_creating_a_cloudkit_database.
Nov ’21
Reply to Why, when assigning a parent record to a shared child record, is the parent record itself changed?
Thank you. Are you suggesting that the parent reference for childA is reused for childB, and the mismatch between the pre-Operation2 parent and the post-Operation1 parent reference is what's causing the error? In other words, that the creation of a parent reference updates/changes the referenced parent, perhaps to update its modification date?
Nov ’21
Reply to Deleting/Leaving a share
I should add that I'm trying to understand how this works independently of the sharing controller. I appreciate that the sharing controller offers a "Stop Sharing" button for the owner and a "Remove Me" button for the joined participant. I was wondering about the programmatic approach.
Jul ’20