0

I'm trying to share a record with other users in CloudKit but I keep getting an error. When I tap one of the items/records on the table I'm presented with the UICloudSharingController and I can see the iMessage app icon, but when I tap on it I get an error and the UICloudSharingController disappears, the funny thing is that even after the error I can still continue using the app.

Here is what I have.

Code

var items = [CKRecord]()
var itemName: String?

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let item = items[indexPath.row]

    let share = CKShare(rootRecord: item)

    if let itemName = item.object(forKey: "name") as? String {
        self.itemName = item.object(forKey: "name") as? String
        share[CKShareTitleKey] = "Sharing \(itemName)" as CKRecordValue?
    } else {
        share[CKShareTitleKey] = "" as CKRecordValue?
        self.itemName = "item"
    }
    share[CKShareTypeKey] = "bundle.Identifier.Here" as CKRecordValue
    prepareToShare(share: share, record: item)
}


private func prepareToShare(share: CKShare, record: CKRecord){

    let sharingViewController = UICloudSharingController(preparationHandler: {(UICloudSharingController, handler: @escaping (CKShare?, CKContainer?, Error?) -> Void) in

        let modRecordsList = CKModifyRecordsOperation(recordsToSave: [record, share], recordIDsToDelete: nil)

        modRecordsList.modifyRecordsCompletionBlock = {
            (record, recordID, error) in

            handler(share, CKContainer.default(), error)
        }
        CKContainer.default().privateCloudDatabase.add(modRecordsList)
    })

    sharingViewController.delegate = self

    sharingViewController.availablePermissions = [.allowPrivate]
    self.navigationController?.present(sharingViewController, animated:true, completion:nil)
}



// Delegate Methods:
func cloudSharingControllerDidSaveShare(_ csc: UICloudSharingController) {
    print("saved successfully")
}
func cloudSharingController(_ csc: UICloudSharingController, failedToSaveShareWithError error: Error) {
    print("failed to save: \(error.localizedDescription)")// the error is generated in this method
}
func itemThumbnailData(for csc: UICloudSharingController) -> Data? {
    return nil //You can set a hero image in your share sheet. Nil uses the default.
}
func itemTitle(for csc: UICloudSharingController) -> String? {
    return self.itemName
}

ERROR

Failed to modify some records

Here is what I see...

enter image description here

Any idea what could be wrong?

EDIT:

By the way, the error is generated in the cloudSharingController failedToSaveShareWithError method.

fs_tigre
  • 10,650
  • 13
  • 73
  • 146

3 Answers3

1

Possibly it is from the way you're trying to instantiate the UICloudSharingController? I cribbed my directly from the docs and it works:

let cloudSharingController = UICloudSharingController { [weak self] (controller, completion: @escaping (CKShare?, CKContainer?, Error?) -> Void) in
    guard let `self` = self else {
       return
    }
    self.share(rootRecord: rootRecord, completion: completion)
}

If that's not the problem it's something with either one or both of the records themselves. If you upload the record without trying to share it, does it work?

EDIT TO ADD:

What is the CKShareTypeKey? I don't use that in my app. Also I set my system fields differently:

share?[CKShare.SystemFieldKey.title] = "Something"
Brian M
  • 3,812
  • 2
  • 11
  • 31
  • Would you mind showing the code that is working for you, I'm honestly lost. The code came from the following tutorial... https://code.tutsplus.com/tutorials/building-a-shopping-list-application-with-cloudkit-sharing-shopping-items--cms-31409 – fs_tigre Apr 28 '20 at 22:34
  • @ Brian M - I tried using the code snippet from the Apple documentation but I got two errors. https://developer.apple.com/documentation/uikit/uicloudsharingcontroller **1-** *Error saving record to server: Shares cannot exist in the default zone* **2-** *Error saving record to server: Can't share records in the default zone*. **Any idea what could be?** – fs_tigre Apr 29 '20 at 22:57
  • 1
    That's it right there - you can't share anything from your private database unless it is in a custom zone. Default zone doesn't allow sharing. – Brian M Apr 30 '20 at 18:04
  • @ Brian M Right now I don't have any custom zones, the records are saved directly in the `_defaultZone`. Do you think I need to be saving the records in my `Private Database` in a custom zone? – fs_tigre Apr 30 '20 at 18:16
  • Yes that's your problem. I've put a link to the docs in a new answer. – Brian M Apr 30 '20 at 20:29
1

Looks like you're trying to share in the default zone which isn't allowed. From the docs here

Sharing is only supported in zones with the CKRecordZoneCapabilitySharing capability. The default zone does not support sharing.

So you should set up a custom zone in your private database, and save your share and records there.

Brian M
  • 3,812
  • 2
  • 11
  • 31
  • Ok, it starting to make sense. If I'm understanding this correctly if you are going to share your `CKRecords` they need to be saved in the `PrivateDatabase` under a custom `Zone` and the same goes for the `CKShare`. Now, is there anything special about the custom zone? In other words, are there any special settings that need to be turned on in the `CloudKit Dashboard` – fs_tigre Apr 30 '20 at 20:39
  • 1
    Nothing special. I'd highly recommend watching the WWDC videos about CloudKit, specifically the 2016 CloudKit Best Practices one: https://developer.apple.com/videos/play/wwdc2016/231/. And skim through the quick start guide: https://developer.apple.com/library/archive/documentation/DataManagement/Conceptual/CloudKitQuickStart/Introduction/Introduction.html#//apple_ref/doc/uid/TP40014987 – Brian M Apr 30 '20 at 20:58
  • Creating the custom zone and using that instead of the _defaultZone, resolved the error. Thanks. – fs_tigre May 02 '20 at 10:33
  • @ Brian M. Could you please clarify the following: I started to save every single record in the app in the `CustomZone` and now I can share records with other users, the issue now is that when I test the app in a second device and try to create new records, that device does not have access to the `CustomZone`. Am I supposed use in the `_defaultZone` as the main zone for all users and only save the items that need to be shared in the `CustomZone`? Is that the process? – fs_tigre May 02 '20 at 12:22
  • To clarify/answer my question above, I was under the impression that once you create a custom zone using the `CloudKit Dashboard` it would be available in all devices using the app; wrong assumption. It appears that any custom zone will need to be created in code for each user. Custom zones created using the `CloudKit Dashboard` are only visible in the testing device, at least is how I understand it. – fs_tigre May 03 '20 at 12:02
  • 1
    @fs_tigre - I would recommend watching those WWDC videos and/or going over the quick start guide to get a general overview of how CloudKit operates. The public database is available for everyone using your app, and each user has a private database. Within that private database you create custom zones and you can share records from those custom zones. Any custom zone you create in a private zone will be available to that user on any device they are logged in on. If you create a custom zone in dashboard, it's in the private database of that Apple ID, and only visible to that account. – Brian M May 03 '20 at 14:57
  • I have watched all videos; they are very helpful, but for some reason I thought that zones needed to be created in the dashboard like the db schema, but it now make sense. – fs_tigre May 03 '20 at 15:01
0

Try to add this to your info.plist

<key>CKSharingSupported</key>
<true/>
qwerty-reloader
  • 151
  • 2
  • 8