简体   繁体   中英

iOS15: NSPersistentCloudKitContainer: how to un-share objects?

I'm working on an app that uses the new sharing support in iOS 15 using 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. In the past, with my own code, I'd use UISharingController s delegate to get notified that the user stopped sharing, and then update my model. But there doesn't seem to be a way to tell Core Data about the change.

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?

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.

Another workaround to the issue is to create a copy of the object being shared, and then deleting the original. It's not elegant, but it works in the meantime. (Hopefully Apple will address this at some point with a better solution.) The copy will be placed in the default zone like it was before you shared the original. You'll be left with an empty share zone, but I dealt with that by running a background task upon app launch that deletes all empty share zones, so it doesn't get out of hand.

I haven't tried the solution posted by Dharman, but I imagine it's slower than querying the local cache. Using the method above, you can still use the "isShared" code from the demo app.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM