2

J'essaie de vérifier si les notifications utilisateur sont activées et sinon je veux lancer une alerte. J'ai donc une fonction checkAvailability qui vérifie plusieurs choses, y compris le statut d'autorisation UserNotification.Attendez que le gestionnaire d'achèvement se termine - 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 
    } 
} 

Mais le gestionnaire d'achèvement est appelé trop tard. La fonction a déjà renvoyé false et après que le code dans la colsure s'exécute.

J'ai essayé de mettre l'instruction entière UNUserNotificationCenter.current().requestAuthorization() dans une file d'attente de répartition synchrone, mais cela n'a pas fonctionné.

Une autre approche serait de revenir de l'intérieur de la fermeture, mais je ne sais pas comment y parvenir.

+0

ajouter un gestionnaire d'exécution en tant que paramètre à 'checkAvailabilty()' et appellent à la fin du gestionnaire d'achèvement de requestAuthorization. – shallowThought

Répondre

7

Ne pas attendre, utilisez un gestionnaire d'achèvement, pour plus de commodité avec un 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)) 
     } 

    }) 
} 
Et

appeler:

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

Merci a fonctionné parfaitement! Mais une question: qu'est-ce que le «@ escapeing» signifie exactement? – Codey

+0

* La fermeture est passée en argument à une fonction et elle est invoquée après que la fonction retourne *, ce qui signifie que la fermeture est en cours – vadian

+0

Donc quand une fermeture avec l'argument '@ escaping' est appelée, la fonction dans ce cas' checkAvailability 'retourne automatiquement? – Codey

2

Alors que vous avez pensé que ce qui se passe est que la fonction retourne avant que le gestionnaire d'achèvement est appelé. Donc, ce que vous voulez faire est de passer un rappel asynchrone à la fonction checkAvailability afin qu'il callback une fois le gestionnaire d'achèvement est tiré.

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

vous appeler cette fonction comme si ...

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

Gardez à l'esprit que lorsque vous allez présenter l'alerte que vous devrez peut-être expédier explicitement l'appel au thread principal depuis l'achèvement le gestionnaire peut rappeler sur un thread différent. Dans ce cas, voici comment vous voulez appeler la fonction et présenter l'alerte ...

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

Merci pour votre réponse et pour vos conseils pour faire des modifications d'interface utilisateur explicitement dans le fil principal avec 'DispatchQueue.main.async {...} ' – Codey

0

Le bloc de code

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

est appelée immédiatement après l'appel à requestAuthorization(options:completionHandler).

Vous devriez plutôt afficher l'alerte dans le gestionnaire d'achèvement:

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

Votre fonction est checkAvailability ne retourne plus synchrone un Bool, comme l'appel à requestAuthorization(options:completionHandler) est asynchrone.

+0

Merci pour votre réponse, mais cela ne résout pas le problème que je dois retourner une valeur booléenne. C'est pourquoi je suis allé avec l'autre solution. – Codey

0

Une autre alternative est de retourner deux paramètres dans le gestionnaire d'exécution:

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

utilisation

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