Wait for completion handler to finish - Swift

馋奶兔 提交于 2019-12-03 15:25:15

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)
    }
}
MikeG

Ya so as you figured what is happening is that the function returns before the completion handler gets called. So what you want to do is pass an asynchronous callback to the checkAvailability function so it will callback once the completion handler is fired.

    func checkAvailabilty(callback: @escaping (Bool) -> Void) {

    //
    // other checking
    //

        UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound], completionHandler: { (granted, error) in
            if granted {
                callback(true)
            } else {
                callback(false)
            }
        })
    }

you would call this function like so...

    checkAvailability(callback: { (isAvailable) -> Void in
        if isAvailable {
            // notifications are available
        } else {
            // present alert
        }
    })

Keep in mind that when you go to present the alert you may need to explicitly dispatch the call to the main thread since the completion handler may callback on a different thread. In which case this is how you would want to call the function and present the alert...

    checkAvailability(callback: { (isAvailable) -> Void in
        if isAvailable {
            // notifications are available
        } else {
            DispatchQueue.main.async {
                // present alert
            }
        }
    })

The code block

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

gets called immediately after the call to requestAuthorization(options:completionHandler).

You should instead display the alert from within the completion handler:

UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound], completionHandler: { (granted, error) in
    if !granted {
        // Show alert
    }
})

Your function checkAvailability is no longer synchronously returning a Bool, as the call to requestAuthorization(options:completionHandler) is asynchronous.

Another alternative is to return two parameters in the completion handler:

func checkAvailabilty(completion: @escaping (_ granted: Bool, _ error: Error?) -> ()) {
    UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound]) { granted, error in
        completion(granted, error)
    }
}

usage

checkAvailabilty { granted, error in
    guard error == nil else {
        // An Authorization error has occurred. Present an alert to the user with the error description.
        DispatchQueue.main.async {
            let alert = UIAlertController(title: "Alert", message: error?.localizedDescription ?? "Authorization failed. Unknown error.", preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "OK", style: .default))
            self.present(alert, animated: true)
        }
        return
    }
    if granted {
        print("granted")  // authorization was successful
    } else {
        print("denied")  // present alert from the main thread
        DispatchQueue.main.async {
            let alert = UIAlertController(title: "Attention", message: "The App needs you to turn on notifications !!!", preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "OK", style: .default))
            self.present(alert, animated: true)
        }
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!