userDidAcceptCloudKitShareWith Not Called on App Launch

Hoping someone can help here as I have been banging my head on this problem for awhile now. I’m trying to implement CloudKit Sharing in a SwiftUI app to send data objects between users using UICloudSharingController.

I have closely followed Apple’s example from https://developer.apple.com/documentation/cloudkit/shared_records/sharing_cloudkit_data_with_other_icloud_users

I can create the CKShare object and send it between users via Messages no problem. Now here comes the problem… If my app is already running in the background when you tap the share object in Messages, then the CKShare is fetched and extracted no problem at all. However, if the app is NOT running in the background already, then the app will launch (i.e. cold start) but userDidAcceptCloudKitShareWith never gets called, so the share object is never fetched and extracted.

I know the problem is not with creating, fetching, and extracting the CKRecord and CKShare objects because it works just fine if the app is already running. Seems to be something wrong in the app launch setup.

I validated userDidAcceptCloudKitShareWith is not called by manually attaching the debugger and setting a breakpoint on that function, which never gets called in the app launch process unlike when the app was already running.

Interestingly, I installed Apple’s sample app from the link above on my two devices (different iCloud accounts) to validate its implementation actually worked, but I got the same result. I can share those contact objects no problem if the app is already running when I accept the share, but it fails if the app is not already running.

I would like to believe Apple’s sample apps would work as the example implementation, which I mirrored. Maybe it’s something with my Xcode setup? I am running version 13.2.1.

If anyone has any suggestions on how to resolve this issue, I would be most grateful. I scoured the forums and tried everything suggested I could find. My head hurts.

Here is the AppDelegate and SceneDelegate code.

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    print("AppDelegate.didFinishLaunchingWithOptions")
    return true
  }

  // MARK: UISceneSession Lifecycle

  func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
    // Called when a new scene session is being created.
    // Use this method to select a configuration to create the new scene with.
    print("AppDelegate.configurationForConnecting")
    return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
  }

  func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
    // Called when the user discards a scene session.
    // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
    // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
  }
}


// SceneDelegate
class SceneDelegate: UIResponder, UIWindowSceneDelegate {

  var window: UIWindow?
  @ObservedObject var cloudKitManager = CloudKitManager()


  func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
     
    print("SceneDelegate.willConnectTo")
          
    let contentView = ContentView()

    if let windowScene = scene as? UIWindowScene {
      let window = UIWindow(windowScene: windowScene)
      window.rootViewController = UIHostingController(rootView: contentView)
      self.window = window
      window.makeKeyAndVisible()
    }
  }

  func windowScene(_ windowScene: UIWindowScene, userDidAcceptCloudKitShareWith cloudKitShareMetadata: CKShare.Metadata) {

    print("MySceneDelegate: userDidAcceptCloudKitShareWith")
     
    guard cloudKitShareMetadata.containerIdentifier == cloudKitManager.containerIdentifier else {
      print("Shared container identifier \(cloudKitShareMetadata.containerIdentifier) did not match known identifier.")
      return
    }
         
    let acceptShareOperation: CKAcceptSharesOperation = CKAcceptSharesOperation(shareMetadatas: [cloudKitShareMetadata])
     
    debugPrint("Accepting CloudKit Share with metadata: \(cloudKitShareMetadata)")
     
    acceptShareOperation.qualityOfService = .userInteractive
    acceptShareOperation.perShareCompletionBlock = {meta, share, error in
      print("CloudKit share was accepted")
      // Hold the accepted share's metadata so it can be fetched next
      let rootRecordID = meta.rootRecordID
      if let error = error {
        debugPrint("Error accepting share with root record ID: \(rootRecordID), \(error)")
      } else {
        debugPrint("Accepted CloudKit share for root record ID: \(rootRecordID)")
        self.cloudKitManager.sharesAccepted.append(cloudKitShareMetadata)
      }
    }
    acceptShareOperation.acceptSharesCompletionBlock = { error in
      if let error = error {
        debugPrint("Error accepting CloudKit Share: \(error)")
      } else {
          self.cloudKitManager.fetchSharedRecords()
        }
    }
    cloudKitManager.container.add(acceptShareOperation)
  }
}

@Steepz Have you found a solution? I have the same issue and can not find a way around it. Even the latest Apple Sample Code behaves like that, i.e. not triggering userDidAcceptCloudKitShareWith in SceneDelegate on the apps first/cold launch. On subsequent launches the delegate method gets triggered!?(https://developer.apple.com/documentation/coredata/sharing_core_data_objects_between_icloud_users)

@Steepz I found a solution. Use the scene(_, willConnect, options) Scene Delegate callback and extract the share metadata like so:

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {

        guard let cloudKitShareMetadata = connectionOptions.cloudKitShareMetadata else { return }

        // Do something with the cloudKitShareMetadata...

    }
userDidAcceptCloudKitShareWith Not Called on App Launch
 
 
Q