iosswiftstoryboardsegue

Navigating UITabController from UIDelegate on Notification action


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?


Solution

  • 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.