Open a view controller when a iOS push notification is received

55,415

Solution 1

You may be having issues with the if (applicationIsActive) condition.

Put a breakpoint on -didReceiveRemoteNotification and see whether it executes in different scenarios and see if it goes within the if-condition.

(unrelated to a certain extent but worth checking) this question:
didReceiveRemoteNotification when in background


Note:

-didReceiveRemoteNotification will not execute if your app was (initially) closed and you clicked on the push notification to open the app.
This method executes when a push notification is received while the application is in the foreground or when the app transitions from background to foreground.

Apple Reference: https://developer.apple.com/documentation/uikit/uiapplicationdelegate

If the app is running and receives a remote notification, the app calls this method to process the notification. Your implementation of this method should use the notification to take an appropriate course of action.
...
If the app is not running when a push notification arrives, the method launches the app and provides the appropriate information in the launch options dictionary. The app does not call this method to handle that push notification. Instead, your implementation of the application:willFinishLaunchingWithOptions: or application:didFinishLaunchingWithOptions: method needs to get the push notification payload data and respond appropriately.


So... When the app is not running and a push notification is received, when the user clicks on the push notification, the app is launched and now... the push notification contents will be available in the -didFinishLaunchingWithOptions: method in it's launchOptions parameter.

In other words... -didReceiveRemoteNotification won't execute this time and you'll also need to do this:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    //...
    NSDictionary *userInfo = [launchOptions valueForKey:@"UIApplicationLaunchOptionsRemoteNotificationKey"];
    NSDictionary *apsInfo = [userInfo objectForKey:@"aps"];

    if(apsInfo) {
        //there is some pending push notification, so do something
        //in your case, show the desired viewController in this if block
    }
    //...
}

Also read Apple's Doc on Handling Local and Remote Notifications

Solution 2

There is an extra space in the identifier name. Remove it and try:

UIStoryboard *mainstoryboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];
PushBildirimlerim* pvc = [mainstoryboard instantiateViewControllerWithIdentifier:@"PushBildirimlerim"];
[self.window.rootViewController presentViewController:pvc animated:YES completion:NULL];

Solution 3

In Swift 4

If you need to achieve the above case you have to handle 2 cases

  1. When your app is in the background/Foreground state(if push notification is not silenced)
  2. When your app is in the inactive state

Here I am using category(built in parameter in the payload of push notification to identify the type of notification) if there are more than 1 type of notifications. In case you have only 1 type of notification then no need to check for the category.

So for handling the first case, the code is as follows in AppDelegate File

     func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {  
     let userInfo = response.notification.request.content.userInfo  
     let title = response.notification.request.content.title 

  //Method- 1 -- By using NotificationCenter if you want to take action on push notification on particular View Controllers
     switch response.notification.request.content.categoryIdentifier  
     {  
      case "Second":  
       NotificationCenter.default.post(name: NSNotification.Name(rawValue: "SecondTypeNotification"), object: title, userInfo: userInfo)  
       break  
      case "Third":  
       NotificationCenter.default.post(name: NSNotification.Name(rawValue: "ThirdTypeNotification"), object: title, userInfo: userInfo)  
       break  
       default:  
        break  
     }

///Method -2 --- Check the view controller at the top and then push to the required View Controller


            if let currentVC = UIApplication.topViewController() {
                //the type of currentVC is MyViewController inside the if statement, use it as you want to
                if response.notification.request.content.categoryIdentifier == "Second"
                {
                    let storyboard = UIStoryboard(name: "Main", bundle: nil)
                    let vc: SecondViewController = storyboard.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
                    currentVC.navigationController?.pushViewController(vc, animated: true)
                }
                else if response.notification.request.content.categoryIdentifier == "Third"
                {
                    let storyboard = UIStoryboard(name: "Main", bundle: nil)
                    let vc: ThirdViewController = storyboard.instantiateViewController(withIdentifier: "ThirdViewController") as! ThirdViewController
                    currentVC.navigationController?.pushViewController(vc, animated: true)
                }

            }



     completionHandler()  }  

For Method 1- After which you have to add the observers in the default view controller as follows in viewDidLoad

     NotificationCenter.default.addObserver(self,selector: #selector(SecondTypeNotification),  
                     name: NSNotification.Name(rawValue: "SecondTypeNotification"),  
                     object: nil)
     NotificationCenter.default.addObserver(self,selector:#selector(ThirdTypeNotification),  
                     name: NSNotification.Name(rawValue: "ThirdTypeNotification"),  
                     object: nil) 

For Method 1- And also need two add the Notification observer function for adding actions to be executed with the same name used in Observer.

    // Action to be taken if push notification is opened and observer is called while app is in background or active
     @objc func SecondTypeNotification(notification: NSNotification){  
 DispatchQueue.main.async  
   {  
     //Land on SecondViewController  
     let storyboard = UIStoryboard(name: "Main", bundle: nil)  
     let vc: SecondViewController = storyboard.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController  
     self.navigationController?.pushViewController(vc, animated: true)  
   }   
    }
     @objc func ThirdTypeNotification(notification: NSNotification){  
 DispatchQueue.main.async  
   {  
     //Land on SecondViewController  
     let storyboard = UIStoryboard(name: "Main", bundle: nil)  
     let vc: ThirdViewController = storyboard.instantiateViewController(withIdentifier: "ThirdViewController") as! ThirdViewController  
     self.navigationController?.pushViewController(vc, animated: true)  
   }  
       }

So whenever a notification is opened when the app is in the foreground or background the above will execute and move to respective view controller according to the category in the payload.

Now the second case

We know that when the app is inactive the first function that will be called when the push notification is opened is

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {  
 return true  
       }  

So we have to check in this function whether the app is launched by opening push notification or by clicking the app icon. For this, there is a provision provided to us. The function will look as follows after adding the required code.

        func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {  
     FirebaseApp.configure()  
     if #available(iOS 10.0, *) {  
       // For iOS 10 display notification (sent via APNS)  
       UNUserNotificationCenter.current().delegate = self  
       let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]  
       UNUserNotificationCenter.current().requestAuthorization(  
         options: authOptions,  
         completionHandler: {_, _ in })  
     } else {  
       let settings: UIUserNotificationSettings =  
         UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)  
       application.registerUserNotificationSettings(settings)  
     }  
     // Register the notification categories.  
     application.registerForRemoteNotifications()  
     Messaging.messaging().delegate = self  
     /// Check if the app is launched by opening push notification  
     if launchOptions?[UIApplication.LaunchOptionsKey.remoteNotification] != nil {  
       // Do your task here  
       let dic = launchOptions?[UIApplication.LaunchOptionsKey.remoteNotification] as? NSDictionary  
       let dic2 = dic?.value(forKey: "aps") as? NSDictionary  
       let alert = dic2?.value(forKey: "alert") as? NSDictionary  
       let category = dic2?.value(forKey: "category") as? String  
       // We can add one more key name 'click_action' in payload while sending push notification and check category for indentifying the push notification type. 'category' is one of the seven built in key of payload for identifying type of notification and take actions accordingly  


// Method - 1
       if category == "Second"  
       {  
         /// Set the flag true for is app open from Notification and on root view controller check the flag condition to take action accordingly  
         AppConstants.sharedInstance.userDefaults.set(true, forKey: AppConstants.sharedInstance.kisFromNotificationSecond)  
       }  
       else if category == "Third"  
       {  
        AppConstants.sharedInstance.userDefaults.set(true, forKey: AppConstants.sharedInstance.kisFromNotificationThird)  
       } 


// Method 2: Check top view controller and push to required view controller
 if let currentVC = UIApplication.topViewController() {
       if category == "Second"
       {
         let storyboard = UIStoryboard(name: "Main", bundle: nil)
         let vc: SecondViewController = storyboard.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
         currentVC.navigationController?.pushViewController(vc, animated: true)
       }
       else if category == "Third"
       {
         let storyboard = UIStoryboard(name: "Main", bundle: nil)
         let vc: ThirdViewController = storyboard.instantiateViewController(withIdentifier: "ThirdViewController") as! ThirdViewController
         currentVC.navigationController?.pushViewController(vc, animated: true)
        }

      } 
    }  
     return true  
 }  

For Method 1-
After this, check these flags value in the default view controller in viewdidLoad as follows

        if AppConstants.sharedInstance.userDefaults.bool(forKey: AppConstants.sharedInstance.kisFromNotificationSecond) == true  
     {  
       //Land on SecondViewController  
       let storyboard = UIStoryboard(name: "Main", bundle: nil)  
       let vc: SecondViewController = storyboard.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController  
       self.navigationController?.pushViewController(vc, animated: true)  
       AppConstants.sharedInstance.userDefaults.set(false, forKey: AppConstants.sharedInstance.kisFromNotificationSecond)  
     }  
     if AppConstants.sharedInstance.userDefaults.bool(forKey: AppConstants.sharedInstance.kisFromNotificationThird) == true  
     {  
       //Land on SecondViewController  
       let storyboard = UIStoryboard(name: "Main", bundle: nil)  
       let vc: ThirdViewController = storyboard.instantiateViewController(withIdentifier: "ThirdViewController") as! ThirdViewController  
       self.navigationController?.pushViewController(vc, animated: true)  
       AppConstants.sharedInstance.userDefaults.set(false, forKey: AppConstants.sharedInstance.kisFromNotificationThird)  
     }  

This will achieve the goal to open a particular view controller when the push notification is opened.

You can go through this blog- How to open a particular View Controller when the user taps on the push notification received? for reference.

Solution 4

I was having same problem that when app is suspended/terminated and push notification arrives my app was only opening and not redirecting to specific screen corresponding to that notification the solution is,

in - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions this method the parameter launchOptions tells us if it has the notification by checking that we need to call the method to redirect to specific screen

the code is as below...

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    //your common or any code will be here at last add the below code..

    NSMutableDictionary *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];

    if (notification)
    {
//this notification dictionary is same as your JSON payload whatever you gets from Push notification you can consider it as a userInfo dic in last parameter of method -(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
        NSLog(@"%@",notification);
        [self showOfferNotification:notification];
    }

     return YES;
}

then in the method showOfferNotification:notification you can redirect user to corresponding screen like...

//** added code for notification
-(void)showOfferNotification:(NSMutableDictionary *)offerNotificationDic{
//This whole is my coding stuff.. your code will come here..
    NSDictionary *segueDictionary = [offerNotificationDic valueForKey:@"aps"];

    NSString *segueMsg=[[NSString alloc]initWithFormat:@"%@",[segueDictionary valueForKey:@"alert"]];

    NSString *segueID=[[NSString alloc]initWithFormat:@"%@",[offerNotificationDic valueForKey:@"id"]];

    NSString *segueDate=[[NSString alloc]initWithFormat:@"%@",[offerNotificationDic valueForKey:@"date"]];

    NSString *segueTime=[[NSString alloc]initWithFormat:@"%@",[offerNotificationDic valueForKey:@"time"]];

    NSLog(@"Show Offer Notification method : segueMsg %@ segueDate %@ segueTime %@ segueID %@",segueMsg,segueDate,segueTime,segueID);

    if ([segueID isEqualToString:@"13"]){

        NSString *advertisingUrl=[[NSString alloc]initWithFormat:@"%@",[offerNotificationDic valueForKey:@"advertisingUrl"]];

        NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
        [defaults setObject:segueMsg forKey:@"notificationMsg"];
        [defaults setObject:segueDate forKey:@"notifcationdate"];
        [defaults setObject:segueTime forKey:@"notifcationtime"];
        [defaults setObject:advertisingUrl forKey:@"advertisingUrl"];
        [defaults synchronize];

        navigationController = (UINavigationController *)self.window.rootViewController;
        UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:@"Main_iPhone" bundle: nil];
        FLHGAddNotificationViewController *controller = (FLHGAddNotificationViewController*)[mainStoryboard instantiateViewControllerWithIdentifier: @"offerViewController"];
        [navigationController pushViewController:controller animated:YES];
    }
}

Solution 5

when tap on notification call notification delegate function

func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {

        let nav = UINavigationController()
        nav.navigationBar.isHidden = true
        let first = Router.shared.splashVC()
        let sceond = Router.shared.CustomTabbarVC()
        let third = Router.shared.ProviderDetailsVC()
        sceond.selectedIndex = 2
        nav.viewControllers = [first,sceond,third]
        UIApplication.shared.keyWindow?.rootViewController = nav
        UIApplication.shared.keyWindow?.makeKeyAndVisible()

}
Share:
55,415
cdsoft
Author by

cdsoft

Updated on July 09, 2021

Comments

  • cdsoft
    cdsoft almost 3 years

    I want to open a specific view controller when a user clicks on the received push notification message, but when I receive a push notification message and click the message, only the application opens, but it does not redirect to a specific view controller.

    My code is

    -(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
        if (applicationIsActive) {
            UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Bildirim"
                                                                message:[NSString stringWithFormat:@"%@ ",[[userInfo objectForKey:@"aps"] objectForKey:@"alert"]]
                                                               delegate:self cancelButtonTitle:@"Ok" 
                                                      otherButtonTitles:nil];
            [alertView show];
    
            UIViewController *vc = self.window.rootViewController;
            PushBildirimlerim *pvc = [vc.storyboard instantiateViewControllerWithIdentifier:@"PushBildirimlerim "];
    
            [vc presentViewController:pvc animated:YES completion:nil];
         }
    }
    

    My question is related with the iOS push notifications.

  • zbMax
    zbMax over 10 years
    If your app is in background and enters foreground because user touches a push notification, app does not pass by appliaction:didFinishLaunchingWithOptions (because it is already launched) and triggers application:didReceiveRemoteNotification:
  • Toseef Khilji
    Toseef Khilji over 10 years
    when application is closed didReceiveRemoteNotification is not called, it is called when your app is in active state or when you tap on notification from notification center.
  • cdsoft
    cdsoft over 10 years
    Which method should i use that time according to you?
  • Toseef Khilji
    Toseef Khilji over 10 years
    please read @staticVoidMan answer properly, he had explained it.
  • cdsoft
    cdsoft over 10 years
    Thanks for your reply, I used your code and also I tried if( [apsInfo objectForKey:@"alert"] != NULL) { Push to view controller} but when application is closed, application stay in main screen not in message view controller
  • staticVoidMan
    staticVoidMan over 10 years
    @cdsoft : i am sorry, i didn't quite follow you. when you click on the push notification and the app launches, it will execute the if-condition but when there are no push notifications pending then your app will launch normally.
  • cdsoft
    cdsoft over 10 years
    Thank you for yor helps, I used your code block like up, I wrote same code in didFinishLaunchingWithOptions and I wrote redirect code to PushBildirimlerim view. If the application is closed and I click new push notification, application opens but not redirect PushBildirimlerim view. I couldn't solve this problem.
  • cdsoft
    cdsoft over 10 years
  • staticVoidMan
    staticVoidMan over 10 years
    @cdsoft : post a new question regarding the -didFinishLaunchingWithOptions issue (because it seems this question was more about -didReceiveRemoteNotification and @Virussmca answered it correctly) ... also take a screenshot of your storyboard and include it in the question as it may help
  • Mohammad Zaid Pathan
    Mohammad Zaid Pathan over 9 years
    @Virussmca, Thanks for you reply but this solved my problem... UIViewController * viewController = (UIViewController*)[[NSClassFromString(@"ViewControllerName"‌​) alloc] init];
  • Smart Home
    Smart Home over 8 years
    I did some tests and see that -[AppDelegate application:didReceiveRemoteNotification:fetchCompletionHand‌​ler:] is called even if app is not running.
  • Pramod Shukla
    Pramod Shukla about 5 years
    what is default view controller? and what happened if i am on 8th controller and after tapping push i have to move on 2nd
  • Nishigandha Ahire
    Nishigandha Ahire about 5 years
    In that case, you will have to add the observers on the 8th controller. Here as an example, I have added to the default view like the dashboard. We can add an observer for push notification more than one view controller depending on the requirement. Let me know if I am wrong.
  • Pramod Shukla
    Pramod Shukla about 5 years
    But in that case we have to add observer in each controller, i do prefer to check top view controller from app delegate and from there push to destination view controller
  • Nishigandha Ahire
    Nishigandha Ahire about 5 years
    Yes. Thank you for updating me with that method. I will update that soon in my answer.
  • Nishigandha Ahire
    Nishigandha Ahire about 5 years
    I have updated the answer with the new method. @PramodShukla Please check if I am wrong.