CloudKit CKShare userDidAcceptCloudKitShareWith Never Fires on Mac App
Asked Answered
K

5

4

I am working on accepting a CKShare in a macOS app in Swift 4. I've already done all the following:

  • Create the CKShare and save it with its rootRecord to CloudKit
  • Add a participant (CKShare.Participant)
  • I've confirmed that the CKShare is on the CloudKit server and that the person I invited has access to it. Here's a screenshot: https://d.pr/i/0sMFQq

When I click the share link associated with the CKShare, it opens my app, but nothing happens and userDidAcceptCloudKitShareWith doesn't fire.

func application(_ application: NSApplication, userDidAcceptCloudKitShareWith metadata: CKShareMetadata) {
  print("Made it!") //<-- This never gets logged :(
  let shareOperation = CKAcceptSharesOperation(shareMetadatas: [metadata])
  shareOperation.qualityOfService = .userInteractive

  shareOperation.perShareCompletionBlock = {meta, share, error in
    print("meta \(meta)\nshare \(share)\nerror \(error)")
  }
  shareOperation.acceptSharesCompletionBlock = { error in
    if let error = error{
      print("error in accept share completion \(error)")
    }else{
      //Send your user to where they need to go in your app
      print("successful share:\n\(metadata)")
    }
  }

  CKContainer.default().add(shareOperation)
}

Is there some kind of URL scheme I have to include in my info.plist? Or perhaps a protocol I need to conform to in my NSApplicationDelegate delegate? I can't, for the life of me, figure out what to do. Thanks in advance!

Update

I've tried a few more things on this. When I open the share link in a web browser, I see this:

enter image description here

Clicking OK makes the screen fade away to this:

Nothing

Not particularly helpful. :) After doing this, the participant's status in CloudKit is still Invited, so the share still hasn't been accepted.

When I click on a share link within Messages, I am shown a popup like this: CloudKit Sharing Popup After I click open, a new copy of my app shows up in the dock, then the app suddenly closes. The crash log states:

Terminating app due to uncaught exception 'CKException', reason: 'The application is missing required entitlement com.apple.developer.icloud-services'

I've tried turning iCloud off and on again in the Capabilities section of Xcode, but nothing changes. I know this exception can't be right because I can start my app normally and use CloudKit all day long. Only the CKShare causes this crash.

This is a mess. Save me, Obi-wan Kenobi, you're my only hope.

Keenan answered 12/6, 2018 at 23:44 Comment(0)
L
3

Yes,

You need to add this to your info.plist.

<key>CKSharingSupported</key>
<true/>

** EDITED ANSWER **

I use this code to share, I don't do it manually... not sure if this is an option under OS X I must confess. I am using iOS.

let share = CKShare(rootRecord: record2S!)
share[CKShareTitleKey] = "My Next Share" as CKRecordValue
share.publicPermission = .none

let sharingController = UICloudSharingController(preparationHandler: {(UICloudSharingController, handler:
                    @escaping (CKShare?, CKContainer?, Error?) -> Void) in
let modifyOp = CKModifyRecordsOperation(recordsToSave:
                        [record2S!, share], recordIDsToDelete: nil)
modifyOp.savePolicy = .allKeys
modifyOp.modifyRecordsCompletionBlock = { (record, recordID,
                        error) in
                        handler(share, CKContainer.default(), error)
                    }
                    CKContainer.default().privateCloudDatabase.add(modifyOp)
                })
sharingController.availablePermissions = [.allowReadWrite,
                                                          .allowPrivate]
sharingController.delegate = self
                sharingController.popoverPresentationController?.sourceView = self.view
            DispatchQueue.main.async {
                self.present(sharingController, animated:true, completion:nil)
            }

This presents an activity controller in which you can choose say email and then send a link. You might also want to watch this video, focus on cloudKit JS right at the beginning.

Watch this WWDC video too https://developer.apple.com/videos/play/wwdc2015/710/ It talks about the cloudkit JSON API, using it you can query what has and what hasn't been shared in a terminal window/simple script perhaps. I did the same when using dropbox API a few years back. Hey you can even use the cloudkit JSON API within your code in place of the native calls.

Lippert answered 13/6, 2018 at 11:48 Comment(3)
Thanks for your suggestion. I already have that key set. I can get everything about CloudKit sharing to work but this app delegate method. :)Keenan
Edited my answer to include more code and some other ideas.Lippert
One final comment I know I am iOS and you are OS X, but I noticed one line that was different. The last one. I got this ... CKContainer(identifier:cloudKitShareMetadata.containerIdentifier).add(acceptShareOperation)Lippert
K
2

I finally got it to work! I did all of the following:

  1. Deleted my app from ~/Library/Developer/Excode/DerivedData
  2. Made sure I had no other copies of my app archived anywhere on my machine.
  3. Said a prayer.
  4. Rebooted.

Sheesh, that was rough. :)

Keenan answered 13/6, 2018 at 19:19 Comment(6)
I am running into a similar situation; wondering if you can help. When I try to open the share link on the Mac, it gives me if I want to open the share link, but when I try it, it says I should update my app to the latest version from the App Store. It apparently doesn't find the debug version from either Applications or DerivedData directories, even after I deleted the App Store version. So did you do anything special to get your app to open? Was your app in the App Store already when you were testing this?Lepidopterous
My app isn’t yet in the App Store, no. I had to make sure there were no other copies of the app laying around. If you deleted your App Store version, make sure its Application Support data is gone, too. It might be worth rebooting and restarting Xcode for good measure after you delete all the app files.Keenan
Thanks for the comment. In my case, I was able to get going by reindexing Launch Services. That helped the system pick the right version of my app. Before I tried that, I had also tried deleting all versions and rebooted a couple of times as well, though it hadn't help.Lepidopterous
@ZS you got to add <key>CKSharingSupported</key> <true/>Elasticize
@CliftonLabrum - Four years later and I'm still having the same issue. It appears very little has been written on this at least from a Mac perspective even now. I have tried restarting everything and I'm still having the same type of issue. Could you please take a quick look at this questions: #72160208 for me and see if I'm missing anything?Launcher
As much as I hate to admit it - this was the answer :)Retrogradation
A
1

If your app is a Mac Catalyst app running on any version of macOS Catalina at least up to and including 10.15.4 Beta 1, a UIApplicationDelegate userDidAcceptCloudKitShareWith method will never be invoked.

After some significant debugging, we discovered that the MacCatalyst UIKit doesn’t even have an implementation for userDidAcceptCloudKitShareWithMetadata in its UIApplication delegate. It’s not broken, it’s just not there. So, at least temporarily, our workaround is the following, which seems to work, even if it’s very inelegant:

// Add CloudKit sharing acceptance handling to UINSApplicationDelegate, which is missing it.
#if targetEnvironment(macCatalyst)
extension NSObject {
    @objc func application(_ application: NSObject, userDidAcceptCloudKitShareWithMetadata cloudKitShareMetadata: CKShare.Metadata) {
        YourClass.acceptCloudKitShare(cloudKitShareMetadata: cloudKitShareMetadata)
    }
}
#endif
Aldus answered 14/2, 2020 at 18:55 Comment(1)
it still works on MacOS Catalina 10.15.7. Did you check if there is any bug report filled about this issue?Lupe
J
1

If you are using a SceneDelegate, implement the delegate callback there, instead of on AppDelegate.

func windowScene(_ windowScene: UIWindowScene, userDidAcceptCloudKitShareWith cloudKitShareMetadata: CKShare.Metadata) {
    // ...
}
Juliettajuliette answered 9/6, 2020 at 17:24 Comment(0)
L
0

You need to create the app delegate for your SwiftUI app using @NSApplicationDelegateAdaptor:

@main
struct Sharing_ServiceApp: App
{
    @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

    var body: some Scene
    {
        WindowGroup
        {
            ContentView()
                .environment(\.managedObjectContext, persistenceController.container.viewContext)
        }
    }
}

I put that line in and my code instantly started receiving the share requests.

Launcher answered 21/5, 2022 at 10:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.