web-dev-qa-db-ja.com

完了ハンドラーが終了するのを待ちます-Swift

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()全体を同期ディスパッチキューに入れようとしましたが、これは機能しませんでした。

別のアプローチは、クロージャーの内側から戻ることですが、それを達成する方法がわかりません。

6
Codey

待たないでください、列挙型で便宜上、完了ハンドラーを使用してください:

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)
    }
}
13
vadian

何が起こっているかを理解したように、完了ハンドラーが呼び出される前に関数が戻るということです。したがって、実行したいのは、非同期コールバックを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
            }
        }
    })
3
MikeG

もう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)
        }
    }
}
0
Leo Dabus

コードブロック

_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は同期的にブール値を返しなくなりました。

0
Dan W