skip to Main Content

I am using FCM for notifications, the app is configured and receiving the notifications correctly and I also know where to put the action related to the notification depending on the data sent in the notification.

The problem is I have no idea on how to make it redirect to the specific ViewController I am using when the notification is clicked.

My Initial View Controller is a regular ViewController of class AcessController that is used to either register or log in the user account.

Once logged in, I redirect the user to the main Tab Controller like this:

func loginExitoso()
{
    self.performSegue(withIdentifier: "loginExSeg", sender: nil)
}

The segue is configured like this:

segue configuration

Inside my AppDelegate I have the following code for the notification action:

extension AppDelegate: UNUserNotificationCenterDelegate {
  // other functions omitted 

  func userNotificationCenter(_ center: UNUserNotificationCenter,
                              didReceive response: UNNotificationResponse) async {
    let userInfo = response.notification.request.content.userInfo
      
      if userInfo["action_location"]! as? String == "horario" {
          self.window?.rootViewController!.performSegue(withIdentifier: "gotoHusoHorarioSegue", sender: nil)    
      }
  }
}

From what I understand the rootViewController in this case would be AccessController, but for some reason the segue thing does nothing, I even added a prepare function inside the AccessController and the segue is specifically connected to the correct screen.

What am I doing wrong?

Is there a way to do this using the UITabBarController because I only have five cases of navigation and they are all tabs from the main TabBar and I just need to pass arguments to one of them?

UPDATE

I tried doing what Petar commented but I wasn’t able to solve this. I am going to give a little more information.

This is part of my storyboard. As you can see my initial ViewController is the one on the left and when login is successful, it redirects to the Main Nav Controller which has the tab navigation.
Storyboard 1

Next, I have a segue from there to the Navigation Controller of one of the sections of the app.
Storyboard 2

That is a relationship segue used for Navigation of the app. It is connected from the Main Nav Controller to the View Controller of that specific section and it has this ID:
Segue selector

I am not sure if there’s a difference between relationship segue and others.

I updated the upper code like this:

let userInfo = response.notification.request.content.userInfo
let storyboard = UIStoryboard(name: "Main", bundle: nil)
      
let viewController = storyboard.instantiateViewController(withIdentifier: "mainNavController") as! UITabBarController
UIApplication.shared.windows.first?.rootViewController = viewController
UIApplication.shared.windows.first?.makeKeyAndVisible()
      
if userInfo["action_location"]! as? String == "lunahoy" {
          
} else if userInfo["action_location"]! as? String == "mes" {
          
} else if userInfo["action_location"]! as? String == "anio" {
          
} else if userInfo["action_location"]! as? String == "horario" {
    viewController.performSegue(withIdentifier: "gotoHusoHorarioSegue", sender: self)
} else if userInfo["action_location"]! as? String == "servicios" {
          
}

When running this I got this error:

Thread 1: "Receiver (<AppName.MainNavController: 0x107833400>) has no segue with identifier 'gotoHusoHorarioSegue'"

Also, I have an issue with the above code where my app’s upper bar (time and stuff) goes black. It’s normally white. This happens because of these lines, but from what I understand this is the only way to switch to that particular controller:

Black top part of screen

let viewController = storyboard.instantiateViewController(withIdentifier: "mainNavController") as! UITabBarController
UIApplication.shared.windows.first?.rootViewController = viewController
UIApplication.shared.windows.first?.makeKeyAndVisible()

Also, one more requirement to the question. In a specific case I need to be able to send an additional parameter through the navigation, how would that work with a relationship segue? I basically need to initialize a variable in one of the View Controllers to select a specific entry. The rest of the sections don’t require this.

Help would be greatly appreciated, because I am running out of ideas. I don’t know if my app’s structure is not adequate for this or I am seriously ignorant on Storboard navigation.

UPDATE 3

I was able to almost get this to work. I made this adjustment in AppDelegate.

let userInfo = response.notification.request.content.userInfo
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier: "mainNavController") as! UITabBarController
UIApplication.shared.windows.first?.rootViewController = viewController
UIApplication.shared.windows.first?.makeKeyAndVisible()

guard let tabBarController = window?.rootViewController as? UITabBarController else {
return
}

if userInfo["action_location"]! as? String == "lunahoy" {
  tabBarController.selectedIndex = 0
  if let tabBarController = self.window?.rootViewController as? UITabBarController,
      let selectedViewController = tabBarController.selectedViewController as? LunaHoy {
      selectedViewController.idEntrada = (userInfo["id_entrada"]! as? Int)!
  }
} else if userInfo["action_location"]! as? String == "mes" {
  tabBarController.selectedIndex = 1
} else if userInfo["action_location"]! as? String == "anio" {
  tabBarController.selectedIndex = 2
} else if userInfo["action_location"]! as? String == "horario" {
  tabBarController.selectedIndex = 3
} else if userInfo["action_location"]! as? String == "servicios" {
  tabBarController.selectedIndex = 4
}

This works correctly, I am just not sure how the execution of selectedViewController.idEntrada works, because it currently does nothing. Is this executed after viewDidAppear. How can I refresh this information? The idEntrada is used in my viewDidAppear function on that Controller and it works, but not when it comes from here. Any recommendations?

2

Answers


  1. You are right that the rootViewController of the window is the AccessController, but seems like there is another view controller (your Tab Controller) which is either pushed onto the stack or presented modally.

    So when you perform your segue gotoHusoHorarioSegue as a response to a notification it must be handled from the currently displayed (top of the stack) view controller – I believe this should be your Tab Controller. That said most probably you need to segue from the Tab Controller to your next VC .

    UPDATE: Contd. from updated question

    So there is indeed a difference between relationship and action segues. Calling performSegue Initiates the segue with the specified identifier from the current view controller's storyboard file. Yours is a relationship segue which isn’t specified by an identifier. They are used so that you can lay out statically your UI components within the Storyboard.

    So instead, you can use set the the concrete tab in your UITabBarController instance.

    Setting this property changes the selected view controller to the one
    at the designated index in the viewControllers array

    To your second point: Even though changing the root of the window works, it is probably not the best approach you want to take. This could cause further problems if say you want to bring the login flow at a later point(user logs out for instance) – you’ll have to keep the state of the visible vc and this kind of solution could quickly transition to unmanageable bug-prone mess. I’d suggest to use UINavigationController. as the root(the initial vc) of your app and then you can arrange your push(show) segues to the various other vcs that you want to navigate to; the push(show) segues could be executed programatically. Also a different but quite popular UX would be to bring up the login flow as a modal presentation and dismiss it once its finished thus revealing the tabbar beneath it.

    Update 2: Contd. from updated question

    In a specific case I need to be able to send an additional parameter
    through the navigation, how would that work with a relationship segue?
    I basically need to initialize a variable in one of the View
    Controllers to select a specific entry. The rest of the sections don’t
    require this.

    As stated above you cannot do that with a relationship segue. You can inject your properties directly to the view controller instance.

    Login or Signup to reply.
  2. You can create an instance of the view controller with the following code.

    let navVC = tabBarController.viewControllers![0] as! UINavigationController
    var lunaHoy = navVC.topViewController as! LunaHoy
    

    Then the problem with the parameter could be that it firstly needs to be casted as a string and then as an integer.

    This could be because when obtaining it from the hash, you may be getting a String pointer, which can be cast to string, but, for it to be cast as an integer, it firstly needs to be converted to string to become a value, instead of a pointer. And finally you can pass it to the viewcontroller as an attribute.

    let idEntradaString = userInfo["id_entrada"]! as? String            
    let idEntradaInt = Int(idEntradaString!)            
            
            
    lunaHoy.idEntrada = idEntradaInt!                        
    tabBarController.selectedIndex = 0
    

    You have to run tabBarController.selectedIndeex = 0 in the end, so that when the change occurs, the variable is already initialized.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search