Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Controlling which view controller loads after receiving a push notification in SWIFT

Once I receive a push notification and swipe to open it, it just opens my app and not the VC I want.

So my question is how do I load the VC I want? I know if the app is open I would move the VC over to another inside the didReceiveRemoteNotification but how do I do it if the app isn't open? or if it is in background mode?

Also I have TWO different push notifications, so therefore I need it to move ONE of TWO different VCs. How can I tell the difference between different push notifactions?

Thanks.

like image 609
Henry Brown Avatar asked Jul 24 '15 12:07

Henry Brown


2 Answers

Updated for Swift 4.2

Like it was said, you want to register to remote notifications 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() } 

There is no way to know in which viewController you will be when you come back from the lockScreen/Background. What I do is I send a notification from the appDelegate. When you receive a remoteNotification, didReceiveRemoteNotification in the appDelegate is called.

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

Depending on your notification payload, you should first make sure it is not nil and then call a NSNotification that will be catched by the viewControllers that should catch this notification. This is also where you could post different kinds of notification based on payload you received. Could look like this, just take it as an example :

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    } 

If you receive a message notification and you have not logged in anymore because the token has expired, then the notification will never be catched in the view controller because it will never be watched.

Now for the part where you catch the notification in the view controller. In the viewWillAppear:

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

Now that you added this observer, each time a notification is called in this controller, the function catchIt will also be called. You will have to implement it in every view controller you want to implement a specific action.

func catchIt(_ userInfo: Notification){      let prefs: UserDefaults = UserDefaults.standard     prefs.removeObject(forKey: "startUpNotif")      if userInfo.userInfo?["userInfo"] != nil{         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)     } } 

Don't forget to unsubscribe from the notifications when leaving the view controller, else the viewController, if still in the stack, will catch the notification and execute it (well you might want to that, but it's safer to know what you are going into). So I suggest unsubscribing in the viewWillDisappear:

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

Doing it this way, you will load the viewController you want. Now we haven't treated all the cases yet. What if you haven't opened your application yet. Obviously, no UIViewController has been loaded, and none of them will be able to catch the notification. You want to know if you received a notification in didFinishLaunchingWithOptions: in the appDelegate. What I do is:

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

Now, you have set a preference saying the application was started using a remote notification. In the controllers that should be loaded first in your application, I suggest doing the following in the viewDidAppear:

override func viewDidAppear(_ animated: Bool) {     super.viewDidAppear(animated)     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])     } } 

Hope it helps. I also made a GitHub repository to illustrate with local notifications : Local Notifications Observer Pattern (similar to Remote notifications). A similar logic can be implemented using the root view Controller Local Notifications Root Pattern, I personally think it will depend on what you want to implement.

These examples are here to illustrate how it can be simply implemented. With bigger projects, you will end up with more complex architectures such as coordinators that that internally utilize similar mechanisms.

like image 130
Swift Rabbit Avatar answered Sep 20 '22 16:09

Swift Rabbit


In addition to @NickCatib's answer, to find out if you received a notification while your app is running and if so, in the foreground or background you need to use this method in your 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   } } 
like image 24
Fred Faust Avatar answered Sep 17 '22 16:09

Fred Faust