'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. 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. 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.
In the following code, notificationManager.notificationAuthorizationStatus always becomes nil in the onAppear method.
Any idea how can I call the reloadAuthorizationStatus() method in the onAppear method in SwiftUI and right after check the notificationAuthorizationStatus property?
NotificationsManager 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.
Call with a 1 Second Delay
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
}
}
}
Solution 1:[1]
With an asynchronous function, you have to wait until it has finished before you can use any of its results. Using a completion handler is a common approach to do just that. 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 ContentView
.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(notificationManager.notificationAuthorizationStatus.publisher) { status in
switch status{
case .denied:
hasAuthorized = false
default:
break
}
}
.onAppear {
notificationManager.reloadAuthorizationStatus()
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
