macoscore-dataswiftuicksharenssharingservice

SwiftUI: Sharing NSSharingService on macOS not receiving share


I have a simple test application that attempts to share a CoreData record with another user using NSSharingService. I can create my share and that works,

enter image description herebut when I try to receive the share it opens up the application but doesn't do anything.

I have added CKSharingSupported to my plist.

I have also followed this link to no avail: CloudKit CKShare userDidAcceptCloudKitShareWith Never Fires on Mac App

Here is my code:

SharingServiceApp:

final class AppDelegate: NSObject, NSApplicationDelegate
{
    func application(_ application: NSApplication, userDidAcceptCloudKitShareWith metadata: CKShare.Metadata)
    {
        print ("userDidAcceptCloudKitShareWith")
        let shareStore = persistenceController.sharedPersistentStore!
        persistenceController.container.acceptShareInvitations(from: [metadata], into: shareStore)
        { _, error in
            if let error = error
            {
                print("acceptShareInvitation error :\(error)")
            }
        }
    }
}

ContentView:

import SwiftUI
import CoreData
import CloudKit

let persistenceController = PersistenceController()

struct ContentView: View {
    @Environment(\.managedObjectContext) private var viewContext

    @FetchRequest(
        sortDescriptors: [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)],
        animation: .default)
    private var items: FetchedResults<Item>

    var body: some View {
        NavigationView {
            List {
                ForEach(items) { item in
                    NavigationLink {
                        VStack
                        {
                            Text("Item at \(item.timestamp!, formatter: itemFormatter)")
                            Button("Share")
                            {
                                shareRecord(item: item)
                            }
                        }
                        
                    } label: {
                        Text(item.timestamp!, formatter: itemFormatter)
                    }
                }
            }
            .toolbar {
                ToolbarItem {
                    Button(action: addItem) {
                        Label("Add Item", systemImage: "plus")
                    }
                }
            }
            Text("Select an item")
        }
    }

    private func addItem() {
        withAnimation {
            let newItem = Item(context: viewContext)
            newItem.timestamp = Date()

            do {
                try viewContext.save()
            } catch {
                let nsError = error as NSError
                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
            }
        }
    }
}

private let itemFormatter: DateFormatter = {
    let formatter = DateFormatter()
    formatter.dateStyle = .short
    formatter.timeStyle = .medium
    return formatter
}()

func shareRecord(item: Item)
{
    Task
    {
        if let share = await createShare(item: item)
        {
            let container = CKContainer(identifier: "iCloud.com.xxxxxxxx.sharingservice")
            
            let item = NSItemProvider()
            item.registerCloudKitShare(share, container: container)
            
            DispatchQueue.main.async
            {
                let sharingService = NSSharingService(named: .cloudSharing)
                sharingService!.perform(withItems: [item])
            }
        }
    }
}

private func createShare(item: Item) async -> CKShare?
{
    do
    {
        let (_, share, _) = try await persistenceController.container.share([item], to: nil)
        share[CKShare.SystemFieldKey.title] = "MyApp"
        return share
    }
    catch
    {
        print("Failed to create share")
        return nil
    }
}

Solution

  • OK I have finally managed to get userDidAcceptCloudKitShareWith to be called.

    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.