I’m triggering a request authorization for local notification permissions using a toggle in swiftUI, when the toggle is turned on, the user is requested for permission access, I want the toggle to turn back off when the ‘don’t allow’ button is tapped, I’ve implemented my own way of doing it below by setting the Bool binding back to false and using the @Observable macro in iOS 17 to observe state changes but the view isn’t observing the updated change and the toggle does not turn off.
Here’s a shortened version of my implementation below, could anyone spot what I’m missing here?
Game class
@Observable
final class Game {
var appSettings: AppSettings
init() {
appSettings = AppSettings()
}
@Observable
final class AppSettings {
var reminderEnabled: Bool = false {
didSet {
UserDefaults.standard.set(reminderEnabled, forKey: UserDefaultKeys.reminderEnabled)
}
}
init() {
reminderEnabled = UserDefaults.standard.object(forKey: UserDefaultKeys.reminderEnabled) as? Bool ?? false
}
AppSettingsView
struct AppSettingsView: View {
@Bindable var game: Game
@Bindable var appSettings: AppSettings
var body: some View {
NavigationStack {
VStack {
Form {
Toggle("Daily Reminder", isOn: $appSettings.reminderEnabled)
.onChange(of: appSettings.reminderEnabled) { newValue in
if newValue {
NotificationManager.shared.requestNotificationPermissions { granted in
DispatchQueue.main.async {
if granted {
NotificationManager.shared.scheduleNotification(at: appSettings.reminderTime)
} else {
appSettings.reminderEnabled = false
}
}
}
} else {
NotificationManager.shared.cancelNotification()
}
}
NotificationManager
final class NotificationManager {
static let shared = NotificationManager()
var deniedAlertShown:Bool = false
private init() {}
func requestNotificationPermissions( completion: @escaping(Bool) -> Void) {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { success, error in
if success {
completion(success)
} else if let error {
completion(success)
print("error requesting permissions: (error.localizedDescription)")
}
}
}
func cancelNotification() {
UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
}
2
Answers
So I switched my approach from a completion handler to using async/await as per the suggestion from lorem ipsum and it did help me with my issue, I am able to change the toggle state now depending on the request authorization outcome. Note: this code is obviously not clean, as it'll always set the toggle to off now if denied in the onChange modifier, but that can be fixed separately, this answer is just posted for anyone else curious. Thanks everyone.
In iOS, if you already pressed "Don’t Allow" with any permission. you’ll no longer see it again. You have to allow it in the Setting by yourself.
The best way, you should check that permission is exist or not. If it doesn’t have you should show something to navigate to the Setting.