web-dev-qa-db-fra.com

Contrôle du contrôleur de vue chargé après la réception d'une notification Push dans SWIFT

Dès que je reçois une notification Push et que je la glisse pour l'ouvrir, elle ouvre simplement mon application et non le VC que je veux.

Ma question est donc la suivante: comment charger le VC que je veux? Je sais que si l'application est ouverte, je déplacerais le VC vers un autre élément de la variable didReceiveRemoteNotification, mais comment puis-je le faire si l'application n'est pas ouverte? ou s'il est en mode fond? 

De plus, j'ai deux notifications Push différentes, donc j'en ai besoin pour déplacer UN de deux VC différents. Comment puis-je faire la différence entre les différentes notifications Push?

Merci.

29
Henry Brown

Mis à jour pour Swift 3.0

Comme il a été dit, vous souhaitez vous inscrire aux notifications à distance dans applicationDidLaunchWithOptions: 

 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    let pushSettings = UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
    UIApplication.shared.registerUserNotificationSettings(pushSettings)
    UIApplication.shared.registerForRemoteNotifications()
}

Il n'y a aucun moyen de savoir dans quel viewController vous serez lorsque vous revenez de lockScreen/Background. Ce que je fais, c'est que j'envoie une notification depuis appDelegate. Lorsque vous recevez une notification à distance, la commande didReceiveRemoteNotification dans le appDelegate est appelée.

 func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
    let notif = JSON(userInfo) // SwiftyJSON required 

Selon le contenu de votre notification, vous devez d'abord vous assurer que ce n'est pas nul, puis appeler une notification qui sera interceptée par les viewControllers qui doivent intercepter cette notification. Pourrait ressembler à ceci, prenons-le comme exemple:

if notif["callback"]["type"] != nil{
    NotificationCenter.default.post(name: Notification.Name(rawValue: "myNotif"), object: nil)
    // This is where you read your JSON to know what kind of notification you received, for example :    

}

Par exemple, si vous recevez une notification de message et que vous n'êtes plus connecté parce que le jeton a expiré, la notification ne sera jamais interceptée dans le contrôleur de vue, car elle ne sera jamais visionnée.

Passons maintenant à la partie où vous attrapez la notification dans le contrôleur de vue. Dans la vue, apparaîtra:

 override func viewWillAppear(_ animated: Bool) {
    NotificationCenter.default.addObserver(self, selector: #selector(self.catchIt), name: NSNotification.Name(rawValue: "myNotif"), object: nil)
}

Maintenant que vous avez ajouté cet observateur, chaque fois qu'une notification est appelée dans ce contrôleur, la fonction catchIt sera également appelée. Vous devrez l'implémenter dans chaque contrôleur de vue pour lequel vous souhaitez implémenter une action spécifique.

func catchIt(_ userInfo: Notification){

    if userInfo.userInfo?["userInfo"] != nil{
        let prefs: UserDefaults = UserDefaults.standard
        prefs.removeObject(forKey: "startUpNotif")
        prefs.synchronize()

        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        let vc: RedirectAppInactiveVC = storyboard.instantiateViewController(withIdentifier: "RedirectAppInactiveVC") as! RedirectAppInactiveVC
        self.navigationController?.pushViewController(vc, animated: true)

    }
    else{
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        let vc: RedirectAppActiveVC = storyboard.instantiateViewController(withIdentifier: "RedirectAppActiveVC") as! RedirectAppActiveVC
        self.navigationController?.pushViewController(vc, animated: true)
    }
}

N'oubliez pas de vous désabonner des notifications lorsque vous quittez le contrôleur de vue, sinon le viewController, s'il est toujours dans la pile, intercepte la notification et l'exécute (eh bien, vous voudrez peut-être cela, mais il est plus sûr de savoir dans quoi vous allez ). Je suggère donc de vous désabonner dans la vueWillDisappear:

  override func viewWillDisappear(_ animated: Bool) {
    NotificationCenter.default.removeObserver(self)
}

En procédant de cette façon, vous allez charger le viewController souhaité. Maintenant, nous n'avons pas encore traité tous les cas. Que faire si vous n'avez pas encore ouvert votre application? Bien évidemment, aucun UIViewController n'a été chargé et aucun d'entre eux ne pourra intercepter la notification. Vous voulez savoir si vous avez reçu une notification dans didFinishLaunchingWithOptions: dans l'appDelegate. Ce que je fais c'est:

let prefs: UserDefaults = UserDefaults.standard
    if let remoteNotification = launchOptions?[UIApplicationLaunchOptionsKey.remoteNotification] as? NSDictionary {
        prefs.set(remoteNotification as! [AnyHashable: Any], forKey: "startUpNotif")
        prefs.synchronize()
    }

Vous avez maintenant défini une préférence indiquant que l'application a été démarrée à l'aide d'une notification à distance. Dans les contrôleurs qui doivent être chargés en premier dans votre application, je vous suggère de procéder comme suit dans viewDidAppear:

override func viewDidAppear(animated: Bool) {
    let prefs:UserDefaults = UserDefaults.standard
    if prefs.value(forKey: "startUpNotif") != nil{
        let userInfo: [AnyHashable: Any] = ["inactive": "inactive"]
        NotificationCenter.default.post(name: Notification.Name(rawValue: "myNotif"), object: nil, userInfo: userInfo as [AnyHashable: Any])
    }

J'espère que ça aide. J'ai également créé un référentiel github pour illustrer par des notifications locales: Modèle d'observateur de notifications locales (similaire aux notifications distantes). Une logique similaire peut être implémentée à l'aide de la vue racine Contrôleur Modèle racine des notifications locales , je pense personnellement que cela dépendra de ce que vous souhaitez implémenter.

46
Swift Rabbit

En plus de la réponse de @ NickCatib, pour savoir si vous avez reçu une notification pendant l'exécution de votre application et, le cas échéant, en avant-plan ou en arrière-plan, vous devez utiliser cette méthode dans votre AppDelegate:

func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {


// You can determine your application state by
if UIApplication.sharedApplication().applicationState == UIApplicationState.Active {

// Do something you want when the app is active

} else {

// Do something else when your app is in the background


}
}
6
Fred Faust

Lorsque vous exécutez votre application, vous appelez applicationDidLaunchWithOptions:

UIApplication.sharedApplication().registerUserNotificationSettings ( UIUserNotificationSettings(forTypes: (UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound), categories: nil))



if( launchOptions != nil){
    var notificationDict: AnyObject? = launchOptions?[UIApplicationLaunchOptionsRemoteNotificationKey]
    if(notificationDict != nil){
        handleNotification(notificationDict! as! [NSObject : AnyObject])
    }

}

Ici, vous avez handleNotification, qui est fondamentalement ma fonction personnalisée. J'extrais des données de la notification et les utilise pour afficher le contrôleur correspondant.

Voici un exemple de cela:

let notificationType = userInfo["aps"]!["alert"]!!["some-key-I-Need"]! as! String
var storyboard = UIStoryboard(name: "Main", bundle: nil)
let mainViewController = storyboard.instantiateInitialViewController() as! MyViewController
self.window?.rootViewController  = mainViewController
5
Miknash

J'ai trouvé toutes les réponses ci-dessus très utiles. Pourtant, les plus votés n'ont pas fonctionné pour moi lorsque l'application est désactivée. Plus tard, a tenté de mettre en œuvre les réponses de @NickCatib et @thefredelement combinées et elles ont généré une erreur lors de l'exécution de storyboard.instantiateInitialViewController () - "Impossible de transtyper la valeur de type 'UINavigationController'". J'ai découvert que c'était parce que j'ai un fichier de story-board avec NavController en tant que rootViewController. Pour résoudre ce problème, j'ai créé un nouveau contrôleur de navigation et cela a fonctionné avec un problème: j'ai perdu la navigation correcte pour l'application, et la vue n'a même pas montré le bouton de retour. La solution à mes problèmes était d'utiliser les réponses @NickCatib et @thefredelement, mais d'instancier la vue à l'aide d'un identificateur et de le transmettre, en utilisant rootViewController en tant que UINavigationController, comme indiqué ci-dessous.

let rootViewController = self.window?.rootViewController as! UINavigationController
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let mvc = storyboard.instantiateViewControllerWithIdentifier("MyViewController") as! 
             MyViewController
rootViewController.pushViewController(mvc, animated: true)

Cela a bien fonctionné pour moi et je n'ai pas perdu les propriétés de navigation correctes pour l'application.

2
Pablo

La réponse rapide du lapin est la meilleure.

J'ajouterais qu'il manque encore lorsque l'application vient d'être fermée à l'état actif.

Vous pouvez ajouter dans AppDelegate, dans didFinishedLaunchingWithOptions:

if let notification = launchOptions?[.remoteNotification] as? [AnyHashable : Any] {

            notificationsUserInfo = notification as [AnyHashable : Any]
            serveNotifications = true

        }

Vous pouvez créer une variable globale ou un paramètre utilisateur avec la valeur de la notification et utiliser un indicateur pour indiquer au reste de l'application qu'il existe une notification.

Une fois que mainViewController est visible, vous pouvez effectuer des actions pour traiter les notifications.

override func viewDidAppear(_ animated: Bool) {
        if serveNotifications {
      notificationManager.sharedInstance.processNotification(userInfo: notificationsUserInfo)

            serveNotifications = false

        }

    }

Prends soin.

0
Jorge Cardenas