Search code examples
ioscore-dataswiftuicloudkitimessage

UICloudSharingController does not work for Core Data with CloudKit


In the summer, Apple published an informative sample app on sharing objects between iCloud users using Core Data, CloudKit and UICloudSharingController in SwiftUI.

However, adding more participants using UICloudSharingController does not appear to work when used for Core Data with CloudKit.

MRE:
See the Apple sample app linked above. The same problem also appears when using other sample apps, like the one from RayW. The problem does not appear in samples that use pure CloudKit, such as this one by Apple.

Reproduction:

  1. Create new share,
  2. Manage share with UICloudSharingController,
  3. Share With More People,
  4. Share using Messages or other service. Succeeds on first attempt, fails on subsequent attempts.

Expectation:
We can use UICloudSharingController to add new participants, using Messages, Mail or other platforms. The link will display correctly on all devices.

Reality:
On iOS16+, attempting to share via Messages engages the "Collaboration" framework and leads to an alert: "An Error Occurred. Unable to start collaboration" (see Image 1). A console warning appears (see below). Triggering this error breaks ANY further shares - the link now cannot be created for Mail and other platforms either (see Image 2). Furthermore, if the first attempt succeeds, the link does not appear correctly on the receiving device (see Image 3).

After further testing, UICloudSharingController also fails in iOS15 - it just dismisses the sheet rather than throw an alert on a compact device. UICloudSharingController is definitely bugged when showing an existing share.

Console logs:
The following console message appears when this issue happens for the first time:

  • CoreDataCloudKitShare[3672:1242159] systemSharingUIDidSaveShareBlock received error: <CKError 0x28314d8c0: "Server Record Changed" (14/2004); server message = "client oplock error updating record"; op = 134D57570A63DF3A; uuid = 8F070F8B-0AC0-4FFE-A52D-154BCBF3196C; container ID = “containerID>

Where "containerID" is the CKContainer ID, like “iCloud.com.company.samples.CoreDataCloudKitShare”. The message does not appear on subsequent attempts to add more people.

Question:
How can this be resolved, so that we can share Core Data records between users using CloudKit and UICloudSharingController?

Images

EDITs:

  • Other samples of sharing using Core Data and CloudKit exhibit the same problem. Also, console shows a warning when the issue first happens. Post was edited to reflect this.
  • Question was also posed in Apple Dev Forums
  • Feedback submitted at FB11623246.
  • Added conclusions after further testing
  • 24 October 2022: I thought the issue was resolved in iOS16.0.3, but it continues to manifest. It does not matter whether UICloudSharingController is invoked using UIViewController or UIViewControllerRepresentable.
  • 10 November 2022: The issue appears in both Development and Production environments (tested via TestFlight)
  • 15 November 2022: In Ask Apple office hours, this was brought up to the attention of an Apple engineer, who confirmed this as a regression.
  • 25 November 2022: A DTS has been opened. This is now confirmed as a known bug with high priority.
  • 26 December 2022: The bug remains in iOS16.2. An attempt was made to use SWCollaborationView as an alternative to managing collaboration in iOS16. The sharing workflow indeed goes through even on subsequent shares. However, SWCollaborationView does not appear to be compatible with SwiftUI (ugh). See associated SO question
  • 17 January 2023: SWCollaborationView is confirmed as NOT compatible with SwiftUI. FB submitted at FB11941664, please submit FB as well.
  • 25 January 2023: Unfortunately, the issue persists in iOS 16.3.
  • 15 February 2023: Unfortunately, the issue persists in iOS 16.3.1.
  • 2 March 2023: Apple says the issue should be fixed in the following configuration. I have not yet tested this (maybe next week). If anyone could confirm it would be great.
    • Xcode 14.3 beta 2 (14E5207e)
    • iOS 16.4 beta 2 (20E5223e)
    • iPadOS 16.4 beta 2 (20E5223e)
    • macOS 13.3 beta 2 (22E5230e)
  • 12 March 2023: I tested the above configuration. UICloudSharingController now works as intended. HOWEVER, attempting to add a photo to an existing share where user is Participant with Write privileges results in an error. Apple was notified, waiting for a response. Console output: shareObject(_:to:completionHandler:): Failed to share an object: Error Domain=NSCocoaErrorDomain Code=134060 "A Core Data error occurred." UserInfo={NSLocalizedFailureReason=NSPersistentCloudKitContainer does not support sharing objects across persistent stores. Objects must first be assigned to the correct persistent store (private, or shared as the case may be), so that they can be moved in to the correct record zone for sharing.})
  • 14 March 2023: I re-tested and confirmed the above issue using a fresh fork of the Apple Sample app. If you'd like to reproduce the issue, here are the steps I used
  • 27 April 2023: After some back and forth with Apple, I managed to resolve the issues described in this post, and built a working app with Core Data and CloudKit sharing. On their side, Apple updated the sample app. Mission accomplished.

Solution

  • As of 27 April 2023 and iOS 16.4, the issues described in the post were resolved by Apple.

    After some back and forth with Apple in my TSI, I managed to build a working app with Core Data and CloudKit sharing.

    On their side, Apple updated their sample app to reflect the issues discussed and the updates made to UICloudSharingController. Use the same link to check it out.

    I would like to end this by saying that Apple engineers were very helpful in solving this and provided me with lots of tips. I am grateful for their assistance.


    For others: you might struggle with adding objects to other user's CKShare (i.e. into the shared persistent store). This is a response from Apple on this that set me on the right track:

    "The way to support the use case today is to relate the photo to an object that is already tied to a share (a shared tag, for example). That way, Core Data assigns the photo to the right record zone when saving it, just like what it does when you add a new tag to a shared photo from the participant side. It may be reasonable to introduce something like albums so users can tie a photo to a shared album."

    Also, if you plan to show the same object between shares (e.g. a single Photo that might show up in multiple Albums), do not forget that relationships cannot be established across shared record zones. So you cannot have a single Photo with a relationship to multiple Albums.