使用 UIApplicationDelegateAdaptor 从 userDidAcceptCloudKitShareWith 获取回调不起作用

Gar*_*abo 5 icloud swift cloudkit cloudkit-sharing swiftui

我试图在userDidAcceptCloudKitShareWith被调用时得到通知。传统上这是在 中调用的,App Delegate但是因为我正在构建一个 iOS 14+App用作我的根对象。关于如何添加userDidAcceptCloudKitShareWith到我的 App 类,我还没有找到任何文档,所以我正在UIApplicationDelegateAdaptor使用一个App Delegate类,但是它似乎userDidAcceptCloudKitShareWith从来没有被调用过?

import SwiftUI
import CloudKit

// Our observable object class
class ShareDataStore: ObservableObject {
    
    static let shared = ShareDataStore() 
    @Published var didRecieveShare = false
    @Published var shareInfo = ""
}

@main
struct SocialTestAppApp: App {
    
    
    @StateObject var shareDataStore = ShareDataStore.shared 
    
    
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    
    var body: some Scene {
        WindowGroup {
            ContentView().environmentObject(shareDataStore)
        }
    }
}

class AppDelegate: NSObject, UIApplicationDelegate {
    
    let container = CKContainer(identifier: "iCloud.com.TestApp")
    
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        print("did finish launching called")
        return true
    }
    
    
    func application(_ application: UIApplication, userDidAcceptCloudKitShareWith cloudKitShareMetadata: CKShare.Metadata) {
        print("delegate callback called!! ")
        acceptShare(metadata: cloudKitShareMetadata) { result in
            switch result {
            case .success(let recordID):
                print("successful share!")
                ShareDataStore.shared.didRecieveShare = true
                ShareDataStore.shared.shareInfo = recordID.recordName
            case .failure(let error):
                print("failure in share = \(error)")
            }
        }    }
    
    func acceptShare(metadata: CKShare.Metadata,
                     completion: @escaping (Result<CKRecord.ID, Error>) -> Void) {
        
        // Create a reference to the share's container so the operation
        // executes in the correct context.
        let container = CKContainer(identifier: metadata.containerIdentifier)
        
        // Create the operation using the metadata the caller provides.
        let operation = CKAcceptSharesOperation(shareMetadatas: [metadata])
        
        var rootRecordID: CKRecord.ID!
        // If CloudKit accepts the share, cache the root record's ID.
        // The completion closure handles any errors.
        operation.perShareCompletionBlock = { metadata, share, error in
            if let _ = share, error == nil {
                rootRecordID = metadata.rootRecordID
            }
        }
        
        // If the operation fails, return the error to the caller.
        // Otherwise, return the record ID of the share's root record.
        operation.acceptSharesCompletionBlock = { error in
            if let error = error {
                completion(.failure(error))
            } else {
                completion(.success(rootRecordID))
            }
        }
        
        // Set an appropriate QoS and add the operation to the
        // container's queue to execute it.
        operation.qualityOfService = .utility
        container.add(operation)
    }
    
    
}
Run Code Online (Sandbox Code Playgroud)

根据 Asperi 的回答更新:

import SwiftUI
import CloudKit

class ShareDataStore: ObservableObject {
    
    static let shared = ShareDataStore() 
    
    @Published var didRecieveShare = false
    @Published var shareInfo = ""
}

@main
struct athlyticSocialTestAppApp: App {
    
    
    @StateObject var shareDataStore = ShareDataStore.shared 
    
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    
    let sceneDelegate = MySceneDelegate()
    
    var body: some Scene {
        WindowGroup {
            ContentView().environmentObject(shareDataStore)
                 .withHostingWindow { window in
                 sceneDelegate.originalDelegate = window.windowScene.delegate
                 window.windowScene.delegate = sceneDelegate
              }
        }
        
    }
    
}


class MySceneDelegate: NSObject, UIWindowSceneDelegate {

    let container = CKContainer(identifier: "iCloud.com...")
    
     var originalDelegate: UIWindowSceneDelegate?

        var window: UIWindow?

        func sceneWillEnterForeground(_ scene: UIScene) {
            print("scene is active")
        }

        func sceneWillResignActive(_ scene: UIScene) {
            print("scene will resign active")
        }
        
    
      // forward all other UIWindowSceneDelegate/UISceneDelegate callbacks to original, like
   func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    originalDelegate?.scene!(scene, willConnectTo: session, options: connectionOptions)
   }

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

            print("delegate callback called!! ")
            acceptShare(metadata: cloudKitShareMetadata) { result in
                switch result {
                case .success(let recordID):
                    print("successful share!")
                    ShareDataStore.shared.didRecieveShare = true
                    ShareDataStore.shared.shareInfo = recordID.recordName
                case .failure(let error):
                    print("failure in share = \(error)")
                }
            }

        }

}


extension View {
    func withHostingWindow(_ callback: @escaping (UIWindow?) -> Void) -> some View {
        self.background(HostingWindowFinder(callback: callback))
    }
}

struct HostingWindowFinder: UIViewRepresentable {
    var callback: (UIWindow?) -> ()

    func makeUIView(context: Context) -> UIView {
        let view = UIView()
        DispatchQueue.main.async { [weak view] in
            self.callback(view?.window)
        }
        return view
    }

    func updateUIView(_ uiView: UIView, context: Context) {
    }
}
Run Code Online (Sandbox Code Playgroud)

Asp*_*eri 5

Scene-based应用程序中,userDidAcceptCloudKitShareWith回调被发布到场景委托,但在 SwiftUI 2.0App-based应用程序中,场景委托由 SwiftUI 本身使用来提供scenePhase事件,但不提供处理主题回调的本机方式。

解决这个问题的可能方法是找到一个窗口并注入自己的场景委托包装器,它将处理userDidAcceptCloudKitShareWith和转发其他人到原始 SwiftUI 委托(以保持标准 SwiftUI 事件工作)。

这是一些基于/sf/answers/4429368191/窗口访问的演示快照(但是您可以使用任何其他更好的方法来获取窗口)

@main
struct athlyticSocialTestAppApp: App {
    @StateObject var shareDataStore = ShareDataStore.shared 
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

    let sceneDelegate = MySceneDelegate()
    
    var body: some Scene {
        WindowGroup {
            ContentView().environmentObject(shareDataStore)
              .withHostingWindow { window in
                 sceneDelegate.originalDelegate = window?.windowScene.delegate
                 window?.windowScene.delegate = sceneDelegate
              }
        }
    }
}

class MySceneDelegate : NSObject, UIWindowSceneDelegate {
   var originalDelegate: UISceneDelegate?

   func windowScene(_ windowScene: UIWindowScene, userDidAcceptCloudKitShareWith cloudKitShareMetadata: CKShareMetadata) {

       // your code here
   }

   // forward all other UIWindowSceneDelegate/UISceneDelegate callbacks to original, like
   func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
       originalDelegate?.scene(scene, willConnectTo: session, options: connectionOptions)
   }
}
Run Code Online (Sandbox Code Playgroud)