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
}
}
}
CodePudding user response:
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()
}