2015-07-24 33 views
26

Dopo aver ricevuto una notifica push e averlo fatto scorrere per aprirlo, apre la mia app e non il VC desiderato.Controllo di quale controller di visualizzazione viene caricato dopo aver ricevuto una notifica push in SWIFT

Quindi la mia domanda è: come carico il VC che voglio? So che se l'app è aperta, sposterei il VC su un altro all'interno di didReceiveRemoteNotification ma come faccio se l'app non è aperta? o se è in modalità background?

Inoltre ho DUE notifiche push diverse, quindi quindi ho bisogno di spostare UNO di DUE VC diversi. Come posso dire la differenza tra diverse notifche push?

Grazie.

risposta

37

aggiornato per Swift 3.0

Come si è detto, si vuole Registe r per le notifiche remote in 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() 
} 

Non c'è modo di sapere in cui viewController si sarà quando si torna dalla lockscreen/Sfondo. Quello che faccio è inviare una notifica dall'appDelegate. Quando si riceve una notifica remota, è stata richiamata la notificaRemoteNotification nell'appDelegate.

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

A seconda di cosa il vostro notifica contiene, si deve prima assicurarsi che non è pari a zero e quindi chiamare una notifica che verrà catturato dalle viewControllers che dovrebbero catturare questa notifica. Potrebbe assomigliare a questo, basta prendere come esempio:

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 :  

} 

Ad esempio, se si riceve un messaggio di notifica e non si è connessi in più perché il token è scaduto, quindi la notifica non sarà mai catturato nel guarda il controller, perché non sarà mai guardato.

Ora per la parte in cui si riceve la notifica nel controller della vista. Nella viewWillAppear:

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

Ora che si è aggiunto questo osservatore, ogni volta che una notifica viene chiamato in questo controller, la funzione catchIt sarà inoltre chiamata. Dovrai implementarlo in ogni controller di visualizzazione che desideri implementare un'azione specifica.

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

Non dimenticare di annullare l'iscrizione alle notifiche al momento di lasciare il controller della vista, altrimenti il ​​viewController, se ancora in pila, prenderà la notifica ed eseguirlo (così si potrebbe desiderare di che, ma è più sicuro per sapere cosa stai andando in). Quindi suggerisco l'annullamento dell'iscrizione nel viewWillDisappear:

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

Facendo in questo modo, potrete caricare il viewController che si desidera. Ora non abbiamo ancora trattato tutti i casi. Cosa succede se non hai ancora aperto la tua applicazione. Ovviamente nessun UIViewController è stato caricato e nessuno di questi sarà in grado di ricevere la notifica. Vuoi sapere se hai ricevuto una notifica in didFinishLaunchingWithOptions: nell'appDelegate. Quello che faccio è:

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

Ora, è stata impostata una preferenza dicendo che l'applicazione è stata avviata utilizzando una notifica remota. Nei regolatori che devono essere caricati prima nella propria applicazione, io suggerisco di fare quanto segue nella 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]) 
    } 

Speranza che aiuta. Ho anche creato un repository github per illustrare le notifiche locali: Local Notifications Observer Pattern (simile alle notifiche Remote). Una logica simile può essere implementata utilizzando la vista radice Controller Local Notifications Root Pattern, personalmente penso che dipenderà da ciò che si desidera implementare.

+2

Ottimo lavoro con questo post davvero buono, ma una cosa che vorrei evitare per questo tipo è usare NSUserDefaults(). puoi semplicemente impostare rootViewController nel delegato dell'app e impostare lì i valori. Oh, e per favore correggi prefs.removeObjectForKey ("startUpNotification") a prefs.removeObjectForKey ("startUpNotif") poiché quella è la chiave che usi – Miknash

+1

Penso che tu abbia ragione a seconda del contesto. Se stai impostando il controller della vista radice, non sarai mai in grado di raggiungere una vista specifica che dovrebbe essere precedente a quella vista. Ad esempio, se una notifica di conversazione rende la conversazione come root e poi vuoi far saltare il controller di navigazione per tornare indietro, sarai bloccato. Non sono sicuro di quello che ho appena detto, se sarai in grado di fare pop se hai cambiato la radice direttamente in quel modo? –

+0

Grazie, anche se solo alcune domande, ancora non sono sicuro di come si distingua esattamente tra le diverse notifiche push? Come una notifica voglio passare a un VC e una notifica a un altro? Anche come @NickCatib ha affermato che solo rootViewController sarebbe molto più semplice di NSUserDefaults? –

5

applicationDidLaunchWithOptions Quando si esegue voi l'applicazione, si sta chiamando:

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

} 

Qui, hanno handleNotification che è fondamentalmente la mia funzione personalizzata dove estrarre i dati dalla notifica e utilizzare tali informazioni per mostrare di controllo corrispondente.

Ecco un esempio di che:

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

grazie! anche se mi sento ancora un po 'perso come sto estraendo i dati ?? –

+0

Bene, dovresti considerare la notifica come è json. come puoi vedere nella seconda parte del codice, nella prima riga estraggo "some-key-I-need". Se non sei sicuro di ciò che è presente nella tua notifica e accederai in modo corrispondente ai tuoi dati. Hai tutto sul formato qui: https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/ApplePushService.html – Miknash

+0

scusa, mi sto ancora perdendo guardando questo? il primo pezzo di codice è destinato a fare cosa? e il secondo? C'è un modo per prendere in considerazione il tipo di notifica push che sto ricevendo e da lì passare al controller di visualizzazione corretto .... –

6

Oltre a @ risposta di NickCatib, per scoprire se hai ricevuto una notifica, mentre la vostra applicazione è in esecuzione e in caso affermativo, in primo piano o sullo sfondo è necessario utilizzare questo metodo nella 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 


} 
} 
+1

Buon punto, con questo if-else e la mia risposta tutte le possibili azioni sono coperte :) ecco un +1 :) – Miknash

0

Ho trovato tutte le risposte sopra per essere molto utile. Eppure, il più votato non ha funzionato per me quando l'app è stata disattivata. Più tardi un tentativo di implementare le risposte di @NickCatib e @thefredelement combinato e hanno generato un errore durante l'esecuzione di storyboard.instantiateInitialViewController() - "Impossibile eseguire il cast del valore di 'UINavigationController'". Ho scoperto che questo è successo perché ho un file storyboard con NavController come rootViewController. Per risolvere questo problema ho creato un nuovo controller di navigazione e questo ha funzionato con un problema: ho perso la corretta navigazione per l'app e la vista non mostrava nemmeno il pulsante Indietro. La risposta ai miei problemi era usare le risposte @NickCatib e @thefredelement, ma per istanziare la vista usando un identificatore e per spingerlo, usando rootViewController come UINavigationController, come mostrato sotto.

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) 

Questo ha funzionato bene per me e non ho perso le proprietà di navigazione corrette per l'app.

0

informazioni complementari alle risposte precedenti.

A seconda del tuo stato è possibile modificare la logica. All'interno del metodo didReceiveRemoteNotification:

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

si può può fare un interruttore come questo

let state = UIApplication.sharedApplication().applicationState switch state { case UIApplicationState.Active: case UIApplicationState.Inactive: case UIApplicationState.Background: }

un eseguire le azioni che si desidera in base allo stato attuale l'applicazione è in.

0

La risposta di Swift Rabbit è la migliore.

Vorrei aggiungere che manca ancora quando l'app viene dallo stato di chiusura allo stato attivo.

È possibile aggiungere in AppDelegate, didFinishedLaunchingWithOptions interna:

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

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

     } 

è possibile creare una variabile globale o USERDEFAULT con il valore della notifica, e utilizzare una bandiera per lasciare che il resto della app che v'è un notifica.

Una volta che il ViewController principale è visibile, è possibile eseguire azioni per elaborare le notifiche.

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

      serveNotifications = false 

     } 

    } 

Fare attenzione.