2

Я пытаюсь проверить, включены ли UserNotifications, и если нет, я хочу бросить предупреждение. Поэтому у меня есть функция checkAvailability, которая проверяет несколько вещей, включая статус авторизации UserNotification.Подождите завершения обработчика завершения - Swift

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() в синхронную очередь отправки, но это не сработало.

Другим подходом было бы вернуться изнутри закрытия, но я не знаю, как это осуществить.

+0

Добавление обработчика завершения в качестве параметра 'checkAvailabilty()' и назвать его в конец обработчика завершения requestAuthorization. – shallowThought

ответ

7

Не ждите, использовать обработчик завершения, для удобства с перечислением:

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) 
    } 
} 
+0

Спасибо, отлично работает! Но один вопрос: что именно означает '@ escaping'? – Codey

+0

* Закрытие передается в качестве аргумента функции, и оно вызывается после того, как функция возвращает *, это означает, что закрытие экранируется – vadian

+0

Итак, когда вызывается вызов с аргументом '@ escaping', функция в этом случае' checkAvailability 'автоматически возвращается? – Codey

2

Ya, так как вы поняли, что происходит, так это то, что функция возвращается до вызова обработчика завершения. Итак, что вы хотите сделать, это передать асинхронный обратный вызов функции 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 
      } 
     } 
    }) 
+0

Спасибо за ваш ответ и за ваш совет внести изменения в пользовательский интерфейс явно в основной поток с помощью 'DispatchQueue.main.async {...} ' – Codey

0

Блочный код

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

Ваша функция checkAvailability больше не синхронно возвращать Bool, как вызов requestAuthorization(options:completionHandler) асинхронный.

+0

Спасибо за ваш ответ, но это не решает проблему, что я должен вернуть значение bool. Вот почему я пошел с другим решением. – Codey

0

Другой альтернативой является возвращение двух параметров в обработчике завершения:

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) 
     } 
    } 
} 
Смежные вопросы