UserNotificationsが有効になっているかどうかを確認しようとしていますが、有効になっていない場合はアラートをスローします。そのため、UserNotificationの承認ステータスなど、複数のことをチェックする関数checkAvailability
があります。
_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
}
}
_
しかし、完了ハンドラーの呼び出しが遅すぎます。関数はすでにfalse
を返し、その後、colsureのコードが実行されます。
ステートメントUNUserNotificationCenter.current().requestAuthorization()
全体を同期ディスパッチキューに入れようとしましたが、これは機能しませんでした。
別のアプローチは、クロージャーの内側から戻ることですが、それを達成する方法がわかりません。
待たないでください、列挙型で便宜上、完了ハンドラーを使用してください:
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))
}
})
}
そしてそれを呼んでください:
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)
}
}
何が起こっているかを理解したように、完了ハンドラーが呼び出される前に関数が戻るということです。したがって、実行したいのは、非同期コールバックをcheckAvailability
関数に渡して、完了ハンドラーが起動されるとコールバックするようにすることです。
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)
}
})
}
この関数を次のように呼び出します...
checkAvailability(callback: { (isAvailable) -> Void in
if isAvailable {
// notifications are available
} else {
// present alert
}
})
アラートを表示する場合、完了ハンドラーが別のスレッドでコールバックする可能性があるため、呼び出しをメインスレッドに明示的にディスパッチする必要がある場合があることに注意してください。この場合、これは関数を呼び出してアラートを表示する方法です...
checkAvailability(callback: { (isAvailable) -> Void in
if isAvailable {
// notifications are available
} else {
DispatchQueue.main.async {
// present alert
}
}
})
もう1つの方法は、完了ハンドラーで2つのパラメーターを返すことです。
func checkAvailabilty(completion: @escaping (_ granted: Bool, _ error: Error?) -> ()) {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound]) { granted, error in
completion(granted, error)
}
}
使用法
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)
}
}
}
コードブロック
_if isNotificationsEnabled {
return true
}
else {
// Throw alert: Remind user to activate notifications
return false
}
_
requestAuthorization(options:completionHandler)
の呼び出しの直後に呼び出されます。
代わりに、完了ハンドラー内からアラートを表示する必要があります。
_UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound], completionHandler: { (granted, error) in
if !granted {
// Show alert
}
})
_
requestAuthorization(options:completionHandler)
の呼び出しは非同期であるため、関数checkAvailability
は同期的にブール値を返しなくなりました。