简体   繁体   English

在 onAppear() 方法中调用异步方法 (getNotificationSettings) 变为 nil - SwiftUI MVVM

[英]Calling an asynchronous method (getNotificationSettings) in the onAppear() method becomes nil - SwiftUI MVVM

I have a NotificationsManager class to manage all notification-related code, including the code to check if the user has turned off local notifications.我有一个NotificationsManager class 来管理所有与通知相关的代码,包括检查用户是否关闭本地通知的代码。 I use this class in multiple parts of my code and it works fine, my issue is that I want to call the getNotificationSettings through the NotificationsManager class in the onAppear method in SwiftUI but it always becomes nil.我在我的代码的多个部分使用这个 class 并且它工作正常,我的问题是我想通过NotificationsManager class 在 SwiftUI 的 onAppear 方法中调用getNotificationSettings但它总是变为零。 I know it has to do something with the fact that the getNotificationSettings is an asynchronous method, but I'm not sure how to call it and get the result on the onAppear method.我知道它与getNotificationSettings是一个异步方法这一事实有关,但我不确定如何调用它并在 onAppear 方法上获得结果。

In the following code, notificationManager.notificationAuthorizationStatus always becomes nil in the onAppear method.在以下代码中, notificationManager.notificationAuthorizationStatusonAppear方法中始终变为nil

Any idea how can I call the reloadAuthorizationStatus() method in the onAppear method in SwiftUI and right after check the notificationAuthorizationStatus property?知道如何在检查notificationAuthorizationStatus属性后立即在SwiftUIonAppear方法中调用reloadAuthorizationStatus()方法吗?

NotificationsManager Class通知管理器 Class

class NotificationsManager: ObservableObject{
    @Published private(set) var notificationAuthorizationStatus: UNAuthorizationStatus?

    func reloadAuthorizationStatus() {
        UNUserNotificationCenter.current().getNotificationSettings { settings in
            DispatchQueue.main.async {
                self.notificationAuthorizationStatus = settings.authorizationStatus
            }
        }
    }
}

ContentView内容视图

struct ContentView: View {
    @StateObject private var notificationManager = NotificationsManager()

    var body: some View {
        NavigationView {
            Form {
                // textFields...
            }
            .onAppear(){
                notificationManager.reloadAuthorizationStatus() 
                // here the notificationAuthorizationStatus is always nil              
                switch notificationManager.notificationAuthorizationStatus{
                case .denied:
                    hasAuthorized = false
                default:
                    break
                }
            }
        }
    }
}

If I add a 1-second delay before calling the notificationAuthorizationStatus it works but I know this is not right since sometimes it may take longer than just a second to get the results.如果我在调用notificationAuthorizationStatus之前添加 1 秒的延迟,它会起作用,但我知道这是不对的,因为有时可能需要比一秒钟更长的时间才能得到结果。

Call with a 1 Second Delay延迟 1 秒呼叫

Works but I don't feel it's the right thing to do可行,但我认为这不是正确的做法

.onAppear(){
    notificationManager.reloadAuthorizationStatus()                
    let secondsDelay = DispatchTime.now() + 1
    DispatchQueue.main.asyncAfter(deadline: secondsDelay){
        switch notificationManager.notificationAuthorizationStatus{
        case .denied:
            hasAuthorized = false
        default:
            break
        }
    }
}

With an asynchronous function, you have to wait until it has finished before you can use any of its results.对于异步 function,您必须等到它完成才能使用它的任何结果。 Using a completion handler is a common approach to do just that.使用completion处理程序是一种常见的方法。 So try this approach:所以试试这个方法:

func reloadAuthorizationStatus(completion: @escaping (Bool) -> ()) {  // <-- here
    UNUserNotificationCenter.current().getNotificationSettings { settings in
        DispatchQueue.main.async {
            self.notificationAuthorizationStatus = settings.authorizationStatus
            completion(true) // <-- here
        }
    }
}

and in ContentViewContentView

    .onAppear {
        notificationManager.reloadAuthorizationStatus { isDone in  // <-- here
            switch notificationManager.notificationAuthorizationStatus{
            case .denied:
                hasAuthorized = false
            default:
                break
            }
        }
    }

Another approach is to leave your code as is, and use this .onReceive :另一种方法是按原样保留代码,并使用此.onReceive

.onReceive(notificationManager.notificationAuthorizationStatus.publisher) { status in
    switch status{
    case .denied:
        hasAuthorized = false
    default:
        break
    }
}
.onAppear {
    notificationManager.reloadAuthorizationStatus()
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM