Reputation: 14070
I am working on accepting a CKShare
in a macOS app in Swift 4. I've already done all the following:
CKShare
and save it with its rootRecord
to CloudKitCKShare.Participant
)CKShare
is on the CloudKit server and that the person I invited has access to it. Here's a screenshot: https://d.pr/i/0sMFQqWhen 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:
Clicking OK makes the screen fade away to this:
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:
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.
Upvotes: 6
Views: 1580
Reputation: 7204
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.
Upvotes: 0
Reputation: 7649
If you are using a SceneDelegate
, implement the delegate callback there, instead of on AppDelegate
.
func windowScene(_ windowScene: UIWindowScene, userDidAcceptCloudKitShareWith cloudKitShareMetadata: CKShare.Metadata) {
// ...
}
Upvotes: 2
Reputation: 3098
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
Upvotes: 1
Reputation: 8995
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.
Upvotes: 3
Reputation: 14070
I finally got it to work! I did all of the following:
~/Library/Developer/Excode/DerivedData
Sheesh, that was rough. :)
Upvotes: 2