使用 UIApplicationDelegateAdaptor 从 userDidAcceptCloudKitShareWith 获取回调不起作用 [英] Using UIApplicationDelegateAdaptor to get callbacks from userDidAcceptCloudKitShareWith not working
问题描述
我正在尝试在 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屋!