skip to Main Content

I’m looking to create a property that will track the user’s notification authorization setting for the application. (The ultimate goal here is to alert the user if they ask for a notification but have declined to receive them from the app.) I’ve tried a variety of solutions, to no avail. The code below throws an error that says "Cannot convert return expression of type ‘Void’ to return type ‘String’".

class LocalNotificationScheduler {
var notificationAuthStatus: String {
                UNUserNotificationCenter.current().getNotificationSettings { settings in
                    switch settings.authorizationStatus {
                        case .authorized:
                            return "authorized"
                        case .provisional:
                            return "provisional"
                        case .notDetermined:
                            return "notDetermined"
                        case .denied:
                           return "denied"
                        default:
                            break
                    } // End of "switch settings.authorizationStatus {"
                } // End of "{ settings in"
            }
             private func requestAuthorization() {
                UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, error in
                    if granted == true && error == nil {
                        self.scheduleNotification()
                    }
                }
            } // End of requestAuthorization() func
            
            func schedulingRequested() {
                UNUserNotificationCenter.current().getNotificationSettings { settings in
                    switch settings.authorizationStatus {
                        case .notDetermined:
                            self.requestAuthorization()
                        case .authorized, .provisional:
                            self.scheduleNotification()
                        case .denied:
                            print("Conflict between request for notification and app permissions!")
                        default:
                            break
                    }
                }
            } // End of schedule() func
            
            private func scheduleNotification() {
                let content = UNMutableNotificationContent()
                content.title = "Scheduled notification"
                content.body = "Test"
                content.sound = .default
                
                // Trigger
                let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 10, repeats: false)
                let request = UNNotificationRequest(identifier: "test", content: content, trigger: trigger)
                
                UNUserNotificationCenter.current().add(request) { error in
                    guard error == nil else { return }
                }
            } // End of scheduleNotification() func
            
            // MARK: - Life Cycle
            init() {
            } // End of init()
             
        } 
    
    struct ContentView: View {
        
        // MARK: - Properties
        // Alert the user that there's a conflict between their request for a notification and the app permissions
        @State var notificationConflict: Bool = false
        
        // MARK: - View
        var body: some View {
            Form {
                Button("Send notification") {
                    
                    let notificationScheduler = LocalNotificationScheduler()
                    print("Step 1: notificationScheduler.notificationAuthStatus = (notificationScheduler.notificationAuthStatus)")
                    
                    if notificationScheduler.notificationAuthStatus == "denied" {
                        notificationConflict = true
                        print("Step 2a: notificationScheduler.notificationAuthStatus WAS denied so...")
                        print("notificationConflict = (notificationConflict)")
                    } else {
                        print("Step 2b: notificationScheduler.notificationAuthStatus was NOT denied so scheduling notification")
                        notificationScheduler.schedulingRequested()
                    }
                    print("Step 3: notificationScheduler.notificationAuthStatus = (notificationScheduler.notificationAuthStatus)")
                    print("Step 3: notificationConflict = (notificationConflict)")
                }
            } // End of Form
        } // End of body view
    }

I’ve also tried to creating a function that looks a lot like the code above but sets the value of notificationAuthStatus rather than returning it. The function doesn’t throw an error, but it runs asynchronously such that the value of notificationAuthStatus is not set when I need to use it.

Any advice about how to proceed is much appreciated!

2

Answers


  1. Chosen as BEST ANSWER

    I think I've come up with a solution that works for my purposes. I'm posting it here in case anyone else has the same challenge and stumbles upon this question. Here's what worked for me:

    1. Determine the notification .authorizationStatus using a method, rather than a computed property.
    2. Call that method both when an instance of the LocalNotificationScheduler class is initialized and when a notification is requested. This combination seems to handle midstream changes to the notification .authorizationStatus if the user changes their settings.
    3. Initialize the LocalNotificationScheduler class when the view loads, rather than when the user requests a notification. This means that there may be some unnecessary overhead for the app - creating an instance of LocalNotificationScheduler when it's not needed - but it also accommodates the time lag in checking the .authorizationStatus, which can cause properties using .authorizationStatus not to update immediately.

    Here's my LocalNotificationScheduler code:

    class LocalNotificationScheduler {
        
        var notificationAuthStatusDenied: Bool = false
        
        // MARK: - Methods
        func determineAuthStatus() {
            UNUserNotificationCenter.current().getNotificationSettings { settings in
                switch settings.authorizationStatus {
                    case .authorized, .provisional, .notDetermined:
                        self.notificationAuthStatusDenied = false
                    case .denied:
                       self.notificationAuthStatusDenied = true
                    default:
                        break
                } // End of "switch settings.authorizationStatus {"
            } // End of "{ settings in"
        } // End of determineAuthStatus() func
        
        private func requestAuthorization() {
            UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, error in
                if granted == true && error == nil {
                    self.scheduleNotification()
                }
            }
        } // End of requestAuthorization() func
        
        func schedulingRequested() {
            UNUserNotificationCenter.current().getNotificationSettings { settings in
                switch settings.authorizationStatus {
                    case .notDetermined:
                        self.requestAuthorization()
                    case .authorized, .provisional:
                        self.scheduleNotification()
                    case .denied:
                        print("Conflict between request for notification and app permissions!")
                    default:
                        break
                }
            }
        } // End of schedule() func
        
        private func scheduleNotification() {
            let content = UNMutableNotificationContent()
            content.title = "Scheduled notification"
            content.body = "Test"
            content.sound = .default
            
            // Trigger
            let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 10, repeats: false)
            let request = UNNotificationRequest(identifier: "test", content: content, trigger: trigger)
            
            UNUserNotificationCenter.current().add(request) { error in
                guard error == nil else { return }
            }
        } // End of scheduleNotification() func
        
        // MARK: - Life Cycle
        init() {
            determineAuthStatus()
        } // End of init()
    }
    

    And the View code:

    struct ContentView: View {
    
    // MARK: - Properties
    var notificationScheduler = LocalNotificationScheduler()
    
    // Alert the user that there's a conflict between their request for a notification and the app permissions
    @State var notificationConflictAlertNeeded: Bool = false
    
    // MARK: - View
    var body: some View {
        Form {
            Button("Send notification") {
            
                // Re-check .authorizationStatus - this doesn't complete until the code below has run,
                // hence the need to first check .authorization status when the notificationScheduler property is initialized.
                // This does mean that the app re-checks the .authorizationStatus and can detect a change during app use
                notificationScheduler.determineAuthStatus()
                
                // Either schedule the notification or set the flag indicating that this isn't possible
                if notificationScheduler.notificationAuthStatusDenied == false {
                    notificationScheduler.schedulingRequested()
                } else {
                    notificationConflictAlertNeeded = true
                }
                
            }
        } // End of Form
    } // End of body view
    }
    

    So, that's my current solution. If there's a better way to do all this, I'd welcome the input!


  2. This compiles:

    var notificationAuthStatus: String {
        var status = "notDetermined"
        UNUserNotificationCenter.current().getNotificationSettings { settings in
            switch settings.authorizationStatus {
            case .authorized:
                status = "authorized"
            case .provisional:
                status = "provisional"
            case .notDetermined:
                status = "notDetermined"
            case .denied:
                status = "denied"
            default:
                break
            } // End of "switch settings.authorizationStatus {"
        } // End of "{ settings in"
        return status
    }
    

    but I don’t know if this suffices for your code as you don’t show it in context, so we don’t know how you are using it.

    If you are using it in a notification controller class to see if you have authorization, or need to request authorization, why not jus use it directly instead of returning a string?

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