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