skip to Main Content

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


  1. Chosen as BEST ANSWER

    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.

    //NotificationManager
    func requestNotificationPermissions()  async -> Bool {
        do {
            return  try await UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound])
        } catch let error {
            print(error.localizedDescription)
        }
        return false
    }
    
    //AppSettingsView
     Toggle("Daily Reminder", isOn: $appSettings.reminderEnabled)
                        .onChange(of: appSettings.reminderEnabled) { newValue in
                            if newValue {
                                Task {
                                    let permissionsGranted = await NotificationManager.shared.requestNotificationPermissions()
                                    if permissionsGranted {
                                        NotificationManager.shared.scheduleNotification(at: appSettings.reminderTime)
                                    } else {
                                        appSettings.reminderEnabled = false 
                                    }
                                }
                            } else {
                                NotificationManager.shared.cancelNotification()
                            }   
    

  2. 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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search