Can't accept CloudKit share invitation from my SwiftUI application

I am able to send invitation from my device to friend's device. When friend clicks on invitation that was shared through text messages it says:

Open "Resources"?

User X wants to collaborate.
You'll join as User Y
(user Y @iCloud.com).

|Not Now|   |Open|

If friend clicks on |Open| then nothing happens. Share remains in "invited" state and the callbacks which I expected to be called are not.

The official Apple CloudKit Sharing App - https://github.com/apple/sample-cloudkit-sharing/blob/main/Sharing/App/AppDelegate.swift - is confusing me because it does not have following code like typical SwiftUI app:

@main
struct MainApp: App {

Instead it uses @main for AppDelegate.

Here is my code with prints that encode what is going on:


class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        print("I see this getting called on App startup")
        return true
    }

    func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
        print("I also see this getting called on App startup")
        return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
    }

    func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
        print("I don't see this getting called")
    }
    
    func application(userDidAcceptCloudKitShareWith cloudKitShareMetadata: CKShare.Metadata) -> Bool {
        print("However, I expected this to be called when friend opened his CloudKit share invitation")
        return false
    }
}

@main
struct MainApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

    static let sharedModelActor: ModelActorDatabase = {
        let schema = Schema([
            Resource.self,
        ])
        let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false, cloudKitDatabase: .none)

        do {
            let modelContainer = try ModelContainer(for: schema, configurations: [modelConfiguration])
            return ModelActorDatabase(modelContainer: modelContainer)
        } catch {
            fatalError("Could not create ModelContainer: \(error)")
        }
    }()
    @StateObject var syncedDatabase: SyncedDatabase = SyncedDatabase(modelContainer: Self.sharedModelActor.modelContainer)

    var body: some Scene {
        WindowGroup {
            ResourceView()
                .environmentObject(syncedDatabase)

        }
        .modelContainer( Self.sharedModelActor.modelContainer )
        .database(SharedDatabase.shared.database)
    }
}

I was expecting that this would call userDidAcceptCloudKitShareWith, but it is not. Why?

Answered by deeje in 800832022

I implemented the userDidAcceptCloudKitShareWith… in the SceneDelegate, and set up the scene delegate as such:

class AppDelegate {

    // MARK: UISceneSession Lifecycle
    
    func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
        
        let config = UISceneConfiguration(name: nil, sessionRole: connectingSceneSession.role)
        
        config.delegateClass = SceneDelegate.self
        
        return config
    }

}

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
        
    var window: UIWindow?
    
    func windowScene(_ windowScene: UIWindowScene, userDidAcceptCloudKitShareWith cloudKitShareMetadata: CKShare.Metadata) {
        let acceptShareOperation = CKAcceptSharesOperation(shareMetadatas: [cloudKitShareMetadata])
        acceptShareOperation.qualityOfService = .userInteractive
        acceptShareOperation.perShareResultBlock = { meta, result in
            guard let recordID = meta.hierarchicalRootRecordID else { return }
            // do the things…
        }
        acceptShareOperation.acceptSharesResultBlock = { result in
            // N/A
        }
        CKContainer(identifier: cloudKitShareMetadata.containerIdentifier).add(acceptShareOperation)
    }
    
}

Accepted Answer

I implemented the userDidAcceptCloudKitShareWith… in the SceneDelegate, and set up the scene delegate as such:

class AppDelegate {

    // MARK: UISceneSession Lifecycle
    
    func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
        
        let config = UISceneConfiguration(name: nil, sessionRole: connectingSceneSession.role)
        
        config.delegateClass = SceneDelegate.self
        
        return config
    }

}

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
        
    var window: UIWindow?
    
    func windowScene(_ windowScene: UIWindowScene, userDidAcceptCloudKitShareWith cloudKitShareMetadata: CKShare.Metadata) {
        let acceptShareOperation = CKAcceptSharesOperation(shareMetadatas: [cloudKitShareMetadata])
        acceptShareOperation.qualityOfService = .userInteractive
        acceptShareOperation.perShareResultBlock = { meta, result in
            guard let recordID = meta.hierarchicalRootRecordID else { return }
            // do the things…
        }
        acceptShareOperation.acceptSharesResultBlock = { result in
            // N/A
        }
        CKContainer(identifier: cloudKitShareMetadata.containerIdentifier).add(acceptShareOperation)
    }
    
}

Can't accept CloudKit share invitation from my SwiftUI application
 
 
Q