NSPersistentCloudKitContainer: how to un-share objects?

I’m working on an app that uses the new sharing support in NSPersistentCloudKitContainer. I do not see a way to tell Core Data that an object, or set of objects, is no longer shared.

For example, if I create a set of objects and then call share(_:to:completion:), the objects are shared properly and moved to a new, custom CKRecordZone, as expected.

Now, if the user stops sharing an object using UICloudSharingController, the CKShare that Core Data created is properly deleted from CloudKit, but the objects are still in the custom zone, and Core Data still has the original CKShare associated with those objects. So, when I call fetchShares(matching:), I still get the CKShare, but of course, that is no longer valid.

Forcing Core Data to fetch CloudKit changes by either moving the app to background and then foreground, or by stopping the app and relaunching does not cause Core Data to notice the change to the share.

Does anyone know how to tell Core Data that these objects are no longer shared?

Note, I’ve tried to do further testing, but iOS 15 beta 4 seems to have broken CloudKit, giving “Account temporarily unavailable due to bad or missing auth token” errors. See this post. Sigh, I’m stuck for now.

  • Any solution to the original "un-share" issue? I am running into the same issue where fetchShares(matching:) still returns the share even after it has been stopped.

Add a Comment

Replies

Got the same problem... Mine works on the iPhone (hardware) and all the simulators, but not my apple watch device. Get the "Account temporarily unavailable due to bad or missing auth token" error. Beta 4.

  • Any solution to the original "un-share" issue? I am running into the same issue where fetchShares(matching:) still returns the share even after it has been stopped.

Add a Comment

I worked around this by always checking to see if the share actually exists in CloudKit, rather than relying on the existence of a CKShare from fetchShares(matching:). I get the URL from the CKShare returned from fetchShares(matching:) and call this:

private func remoteShare(at url: URL) async throws -> CKShare? {
        do {
            let metadata = try await cloudKitContainer.shareMetadata(for: url)
            return metadata.share
        } catch let error as CKError {
            if error.retryable {
                throw RemoteError.retryable
            } else if error.userInterventionRequiredError {
                throw RemoteError.userInterventionRequired
            } else if error.code == .unknownItem {
                return nil
            } else {
                throw RemoteError.remoteFailure
            }
        } catch {
            throw RemoteError.remoteFailure
        }
    }
}

If I get unknownItem that means there is no share on the remote, so the object is not actually shared.

The RemoteError is my custom error handling, and I have some extensions on CKError to categorize the errors.

I hope this helps.