Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wait for completion handler to finish - Swift

I am trying to check if UserNotifications are enabled and if not I want to throw an alert. So I have a function checkAvailability which checks multiple things, including the UserNotification authorization status.

func checkAvailabilty() -> Bool {

    // 
    // other checking
    //

    var isNotificationsEnabled = false
    UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound], completionHandler: { (granted, error) in

                    if granted {
                        isNotificationsEnabled = true
                    }
                    else {
                        isNotificationsEnabled = false
                    }
                })
            }


    if isNotificationsEnabled {
        return true
    }
    else {
        // Throw alert: Remind user to activate notifications
        return false
    }
}

But the completion handler gets called too late. The function already returned false and after that the code in the colsure executes.

I tried to put the whole statement UNUserNotificationCenter.current().requestAuthorization() in a synchronous dispatch queue but this didn't work.

Another approach would be to return from inside the closure but I have no idea how to accomplish that.

like image 263
Codey Avatar asked Feb 15 '17 16:02

Codey


1 Answers

Do not wait, use a completion handler, for convenience with an enum:

enum AuthResult {
    case success(Bool), failure(Error)
}

func checkAvailabilty(completion: @escaping (AuthResult) -> ()) {
    
    //
    // other checking
    //
    UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound], completionHandler: { (granted, error) in
        if error != nil {
            completion(.failure(error!))
        } else {
            completion(.success(granted))
        }
        
    })
}

And call it:

checkAvailabilty { result in
    switch result {
    case .success(let granted) : 
      if granted {
         print("access is granted")
      } else {
         print("access is denied")
      }
    case .failure(let error): print(error)
    }
}

In Swift 5.5 with async/await it does wait indeed

func checkAvailabilty() async throws -> Bool {
    
    //
    // other checking
    //
    
    return try await UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound])
}

And call it:

Task {
    do {
        let granted = try await checkAvailabilty()
        if granted {
           print("access is granted")
        } else {
           print("access is denied")
        }
    } catch {
        print(error)
    }
 }
like image 188
vadian Avatar answered Sep 19 '22 17:09

vadian