使用 UIApplicationDelegateAdaptor 从 userDidAcceptCloudKitShareWith 获取回调不起作用 [英] Using UIApplicationDelegateAdaptor to get callbacks from userDidAcceptCloudKitShareWith not working

查看:19
本文介绍了使用 UIApplicationDelegateAdaptor 从 userDidAcceptCloudKitShareWith 获取回调不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在 userDidAcceptCloudKitShareWith 被调用时收到通知.传统上,这是在 App Delegate 中调用的,但由于我正在使用 App 作为我的根对象构建 iOS 14+.至于如何将 userDidAcceptCloudKitShareWith 添加到我的 App 类,我还没有找到任何文档,所以我使用 UIApplicationDelegateAdaptor 来使用 App Delegate 类,但是似乎没有调用 userDidAcceptCloudKitShareWith ?

导入 SwiftUI导入 CloudKit//我们的可观察对象类类 ShareDataStore:ObservableObject {静态让共享 = ShareDataStore()@Published var didRecieveShare = false@Published var shareInfo = "";}@主要的struct SocialTestAppApp: App {@StateObject var shareDataStore = ShareDataStore.shared@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegatevar主体:一些场景{窗口组{内容视图().环境对象(共享数据存储)}}}类 AppDelegate: NSObject, UIApplicationDelegate {让容器 = CKContainer(标识符:iCloud.com.TestApp")func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) ->布尔{打印(完成启动调用")返回真}func application(_ application: UIApplication, userDidAcceptCloudKitShareWith cloudKitShareMetadata: CKShare.Metadata) {打印(委托回调被调用!!")acceptShare(metadata: cloudKitShareMetadata) { 结果在切换结果{案例.success(让recordID):打印(成功分享!")ShareDataStore.shared.didRecieveShare = trueShareDataStore.shared.shareInfo = recordID.recordName案例.失败(让错误):打印(共享失败= (错误)")}} }func acceptShare(元数据:CKShare.Metadata,完成:@escaping (Result) ->空白) {//创建对共享容器的引用,以便操作//在正确的上下文中执行.让容器 = CKContainer(标识符:metadata.containerIdentifier)//使用调用者提供的元数据创建操作.让操作 = CKAcceptSharesOperation(shareMetadatas: [metadata])var rootRecordID:CKRecord.ID!//如果 CloudKit 接受共享,则缓存根记录的 ID.//完成闭包处理任何错误.operation.perShareCompletionBlock = { 元数据,共享,错误如果让 _ = 分享,错误 == 零 {rootRecordID = metadata.rootRecordID}}//如果操作失败,将错误返回给调用者.//否则,返回共享根记录的记录 ID.operation.acceptSharesCompletionBlock = { 错误如果让错误 = 错误 {完成(.失败(错误))} 别的 {完成(.成功(rootRecordID))}}//设置合适的 QoS 并将操作添加到//容器的队列来执行它.operation.qualityOfService = .utility容器.添加(操作)}}

根据 Asperi 的回答更新:

导入 SwiftUI导入 CloudKit类 ShareDataStore:ObservableObject {静态让共享 = ShareDataStore()@Published var didRecieveShare = false@Published var shareInfo = "";}@主要的struct athlyticSocialTestAppApp: App {@StateObject var shareDataStore = ShareDataStore.shared@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate让sceneDelegate = MySceneDelegate()var主体:一些场景{窗口组{内容视图().环境对象(共享数据存储).withHostingWindow { 窗口在SceneDelegate.originalDelegate = window.windowScene.delegatewindow.windowScene.delegate = sceneDelegate}}}}类 MySceneDelegate: NSObject, UIWindowSceneDelegate {让容器 = CKContainer(标识符:iCloud.com...")var originalDelegate: UIWindowSceneDelegate?变量窗口:UIWindow?funcsceneWillEnterForeground(_场景:UIScene){打印(场景处于活动状态")}funcsceneWillResignActive(_场景:UIScene){打印(场景将主动辞职")}//将所有其他 UIWindowSceneDelegate/UISceneDelegate 回调转发到原始回调,例如func 场景(_ 场景:UIScene,willConnectTo 会话:UISceneSession,选项 connectionOptions:UIScene.ConnectionOptions){originalDelegate?.scene!(scene, willConnectTo: session, options: connectionOptions)}func windowScene(_ windowScene: UIWindowScene, userDidAcceptCloudKitShareWith cloudKitShareMetadata: CKShare.Metadata) {打印(委托回调被调用!!")acceptShare(metadata: cloudKitShareMetadata) { 结果在切换结果{案例.success(让recordID):打印(成功分享!")ShareDataStore.shared.didRecieveShare = trueShareDataStore.shared.shareInfo = recordID.recordName案例.失败(让错误):打印(共享失败= (错误)")}}}}扩展视图{func withHostingWindow(_ callback: @escaping (UIWindow?) -> Void) ->一些视图{self.background(HostingWindowFinder(callback: callback))}}struct HostingWindowFinder: UIViewRepresentable {var 回调:(UIWindow?)->()func makeUIView(context: Context) ->界面视图{让视图 = UIView()DispatchQueue.main.async { [弱视图] 在self.callback(view?.window)}返回视图}func updateUIView(_ uiView: UIView, context: Context) {}}

解决方案

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

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

这是一些基于 https://stackoverflow.com/a/63276688/12299030 窗口的演示快照访问(但是您可以使用任何其他更可取的方式来获取窗口)

@mainstruct athlyticSocialTestAppApp: App {@StateObject var shareDataStore = ShareDataStore.shared@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate让sceneDelegate = MySceneDelegate()var主体:一些场景{窗口组{内容视图().环境对象(共享数据存储).withHostingWindow { 窗口在SceneDelegate.originalDelegate = window?.windowScene.delegatewindow?.windowScene.delegate = sceneDelegate}}}}类 MySceneDelegate : NSObject, UIWindowSceneDelegate {var originalDelegate: UISceneDelegate?func windowScene(_ windowScene: UIWindowScene, userDidAcceptCloudKitShareWith cloudKitShareMetadata: CKShareMetadata) {//你的代码在这里}//将所有其他 UIWindowSceneDelegate/UISceneDelegate 回调转发到原始回调,例如func 场景(_ 场景:UIScene,willConnectTo 会话:UISceneSession,选项 connectionOptions:UIScene.ConnectionOptions){originalDelegate?.scene(scene, willConnectTo: session, options: connectionOptions)}}

I'm trying to get notified when userDidAcceptCloudKitShareWith gets called. Traditionally this was called in the App Delegate but since I am building an iOS 14+ using App as my root object. I couldn't find any documentation out yet as far as how to add userDidAcceptCloudKitShareWith to my App class, so I am using UIApplicationDelegateAdaptor to use an App Delegate class, however it doesn't seem like userDidAcceptCloudKitShareWith is ever getting called?

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)
    }
    
    
}

Updated based on Asperi's Answer:

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) {
    }
}

解决方案

In Scene-based application the userDidAcceptCloudKitShareWith callback is posted to Scene delegate, but in SwiftUI 2.0 App-based application the scene delegate is used by SwiftUI itself to provide scenePhase events, but does not provide native way to handle topic callback.

The possible approach to solve this is to find a window and inject own scene delegate wrapper, which will handle userDidAcceptCloudKitShareWith and forward others to original SwiftUI delegate (to keep standard SwiftUI events working).

Here is a couple of demo snapshots based on https://stackoverflow.com/a/63276688/12299030 window access (however you can use any other preferable way to get window)

@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)
   }
}

这篇关于使用 UIApplicationDelegateAdaptor 从 userDidAcceptCloudKitShareWith 获取回调不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆