How to pass notification.userinfo from PushNotification to a specific viewcontroller in Swift - ios

I have code like this.
if I receive a pushnotification and I tap on it I want to redirect to specific viewcontroller but I want to pass a value (hidden inside notification) to a specific view controller
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive, response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
let application = UIApplication.shared
if(application.applicationState == .inactive)
{
LocalData.indexTabBar = nil
let storyboard = UIStoryboard(name: "Main", bundle: nil)
if LocalData.emailLogin != nil && LocalData.passwordLogin != nil && LocalData.tokenLogin != nil {
let initialViewController = storyboard.instantiateViewController(withIdentifier: "MainControllerIfSavedLogin") as! UITabBarController
initialViewController.selectedIndex = 2 //Selecting tab here
self.window?.rootViewController = initialViewController
} else{
let initialViewController = storyboard.instantiateViewController(withIdentifier: "loginViewController") as UIViewController
self.window?.rootViewController = initialViewController
}
self.window?.makeKeyAndVisible()
}
completionHandler()
}

Get it with
let userInfo = response.notification.request.content.userInfo
Then send it
let tab = ------ as! MainControllerIfSavedLogin
let vc = tab.viewControllers!.first as! LoginViewController
vc.info = userInfo
class LoginViewController : UIViewController {
var info:[String:Any]?
}

Related

How to open tabbar BY click notification

I want to open APP in specific tabbar by click notification on lock screen
Now I used
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void)
{
print("user clicked on the notification")
if let tabBarController = self.window!.rootViewController as? UITabBarController {
tabBarController.selectedIndex = 2
}
}
and it have error
Value of type 'AppDelegate' has no member 'window'
on Swift5, Is it have better way?
This seemed easier and worked for me:
if let tabBarController = UIApplication.shared.windows.filter({$0.isKeyWindow}).first?.rootViewController as? UITabBarController {
tabBarController.selectedIndex = 2
}
solve it!!!
DispatchQueue.main.async{
let keyWindow = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
if var topController = keyWindow?.rootViewController {
while let presentedViewController = topController.presentedViewController {
topController = presentedViewController
let tabbarController = presentedViewController as! UITabBarController
tabbarController.selectedIndex = 2
}
}
}

Navigation bar disappear when click on notification when app is in background state

I need to navigate to a notification controller when user click on notification.Therefore I need to redirect them from AppDelegate class
Here is the code:
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
// Print message ID.
if let messageID = userInfo[gcmMessageIDKey] {
print("Message ID: \(messageID)")
if let cu = self.window?.rootViewController as? UITabBarController
{
print(cu)
let nav: UINavigationController = UINavigationController()
self.window?.rootViewController = nav
let str = UIStoryboard.init(name: "Main", bundle: nil)
let rr = str.instantiateViewController(withIdentifier: "NotificationListViewController")
nav.setViewControllers([cu,rr], animated: true)
}
}
// Print full message.
print(userInfo)
completionHandler()
}
}
The above method is working means navigating to notification page for the first time only When I try with second time It going to dash board page.
The if condition is failing second time(getting nil value).
Help me if Some one knows
You assign UINavigationController to rootViewController the first time you receive notification:
let nav: UINavigationController = UINavigationController()
self.window?.rootViewController = nav
Therefore, when you receive second notification, yout rootViewController is no longer UITabbarController, but rather instance of UINavigationController, so casting
let cu = self.window?.rootViewController as? UITabBarController will fail.
You shouldn't create new UINavigationController. Instead, you should push your view controller
if let cu = self.window?.rootViewController as? UITabBarController {
let str = UIStoryboard.init(name: "Main", bundle: nil)
let rr = str.instantiateViewController(withIdentifier: "NotificationListViewController")
cu.pushViewController(rr, animated: true)
}
or go to proper tab of you UITabBarController:
if let cu = self.window?.rootViewController as? UITabBarController {
let notificationsTabIndex = 1 //use proper tab number
cu.selectedIndex = notificationsTabIndex
}

Properly redirect to a view controller inside tab bar's navigation controller when push notification is clicked

I have a tabBar. Each of its tabs' ViewControllers have been embedded inside their respective NavigationControllers.
Hierarchy:
Tabbarcontroller --> NavigationController --> VC1 --> VC2 -->...VCn (for tab1)
I have to redirect to one of the viewcontrollers say VC2 when i click on push notifications. The code i am using is as follows.
let navigationController = UINavigationController()
let storyboard = UIStoryboard.init(name: "Main", bundle: Bundle.main)
if let chatViewController = storyboard.instantiateViewController(withIdentifier: "chatViewController") as? ChatViewController {
navigationController.viewControllers = [chatViewController]
window?.rootViewController = navigationController
}
By using this, I am redirected to the respective ViewController. However, i am not able to get back to the tabBar. So is it possible to redirect in such a way that it allows me the get back to the tab bar, thus providing the same hierarchy as in the UI?
EDIT:
The image below shows my layout.
I would like to accomplish the following:
When the user taps the push notification, the app should be directed to VC-B. *It should not create new object for VC-B and add to the navigation stack if VC-B is already on top of navigation stack.
If the app had been terminated and the user taps on the notification, it should open VC-B.
For determining if the app had been terminated, I set a flag as:
func applicationWillTerminate(_ application: UIApplication) {
UserDefaults.standard.set(true, forKey: UserDefaultsKey.isTerminated)
}
This flag is set false at the end of didFinishLaunchingWithOptions function.
For redirection, I check this flag to determine if the app had been terminated:
func performRedirectionToSuitableViewController(userInfo: [AnyHashable: Any]) {
let isTerminated = UserDefaults.standard.object(forKey: UserDefaultsKey.isTerminated) as! Bool
if isTerminated {
let storyboard = UIStoryboard.init(name: "Main", bundle: Bundle.main)
let tab = storyboard.instantiateViewController(withIdentifier: "tabBar") as! UITabBarController
tab.selectedIndex = 0
let nav = tab.viewControllers![0] as! UINavigationController
let chatViewController = storyboard.instantiateViewController(withIdentifier: "chatViewController") as! ChatViewController
chatViewController.chatThreadId = userInfo["thread_id"] as? String
nav.pushViewController(chatViewController, animated: true)
} else {
if let tab = window?.rootViewController?.presentedViewController as? UITabBarController {
let storyboard = UIStoryboard.init(name: "Main", bundle: Bundle.main)
let chatViewController = storyboard.instantiateViewController(withIdentifier: "chatViewController") as! ChatViewController
chatViewController.chatThreadId = userInfo["thread_id"] as? String
if let nav = tab.selectedViewController as? UINavigationController {
nav.pushViewController(chatViewController, animated: true)
}
}
}
}
With this code, my first requirement is partially fulfilled. Need to determine if the viewcontroller is at the top of the navigation stack.
And, in the case of terminated app, clicking the push notification opens the tab bar, with the default selection index being selected.
I have spent several days trying to fix this. But cannot get it work.
I suppose you have to do something like this:
let tabBar: UITabBarController // get your tab bar
tabBar.selectedIndex = 0 // eg. zero. To be sure that you are on correct tab
if let navigation = tabBar.viewControllers?[tabBar.selectedIndex] as? UINavigationController {
let storyboard = UIStoryboard.init(name: "Main", bundle: Bundle.main)
if let chatViewController = storyboard.instantiateViewController(withIdentifier: "chatViewController") as? ChatViewController {
navigation.pushViewController(chatViewController, animated: true)
}
}
Well, you are setting current window.rootViewController, which should be that TabBarViewController I'd say. That's why you cannot get back to the TabBar
What you should do is
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
if let chatViewController = storyboard.instantiateViewController(withIdentifier: "chatViewController") as? ChatViewController, let tabBar = storyboard.instantiateViewController(withIdentifier: "tabBar") as? UITabBarController {
let navigationController = UINavigationController(rootViewController: chatViewController)
navigationController.viewControllers = [chatViewController]
tabBar.viewControllers = [navigationController]
window?.rootViewController = tabBar
}
To get the current navigation controller from the tabbar controller. You can use this function
func getVisibleViewController(_ rootViewController: UIViewController?) -> UIViewController? {
var rootVC = rootViewController
if rootVC == nil {
rootVC = UIApplication.shared.keyWindow?.rootViewController
}
if rootVC?.presentedViewController == nil {
return rootVC
}
if let presented = rootVC?.presentedViewController {
if presented.isKind(of: UINavigationController.self) {
let navigationController = presented as! UINavigationController
return navigationController.viewControllers.last!
}
if presented.isKind(of: UITabBarController.self) {
let tabBarController = presented as! UITabBarController
return tabBarController.selectedViewController!
}
//UIAlertController
if presented.isKind(of: UIAlertController.self) {
let alertController = presented as! UIAlertController
return alertController.presentingViewController
}
return getVisibleViewController(presented)
}
return nil
}
Using the function you can navigate your viewcontroller like below.
let mainStoryboardIpad : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let yourView = mainStoryboardIpad.instantiateViewController(withIdentifier: "yourView") as! NewnoteViewController
let navi = self.getVisibleViewController(self.window!.rootViewController) as! UINavigationController
navi.pushViewController(yourView, animated: true)
You can create a method in your RootViewController that will redirect user to a specific view after receiving a push notification. Here's what I did in my previous project.
class RootViewController: UIViewController {
private var currentView: UIViewController
init() {
self.currentView = ViewController()
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
addChildViewController(currentView) // 1
currentView.view.frame = view.bounds // 2
view.addSubview(currentView.view) // 3
currentView.didMove(toParentViewController: self) // 4
}
func showDetailsScreen() {
if let updateDetailView = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "UpdateDetailsNotif") as? UpdateDetailsViewController{
let navController = UINavigationController(rootViewController: updateDetailView)
addChildViewController(navController)
navController.view.frame = view.bounds
view.addSubview(navController.view)
navController.didMove(toParentViewController: self)
currentView.willMove(toParentViewController: nil)
currentView.view.removeFromSuperview()
currentView.removeFromParentViewController()
currentView = navController
}
}
}
Then you can call that method on your AppDelegate like this:
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
let application = UIApplication.shared
AppDelegate.shared.rootViewController.showDetailsScreen()
completionHandler()
}
}

Present viewController over rootViewController when a notification is tapped

When the app is in terminate state and I receive a push notification. Which if I view I want the app to load normally but it should present a viewController over the rootViewController for which I've added a close button and whenever i will tap this button i want simply dismiss this viewController.
In the method which is called when I open the app from notification I've done this:
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void) {
let tabBarController = self.window!.rootViewController as! UITabBarController
self.window?.rootViewController = tabBarController
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
apptVC = storyboard.instantiateViewController(withIdentifier: "NotificationVC") as! NotificationsViewController
window?.makeKeyAndVisible()
window?.rootViewController?.present(apptVC, animated: true, completion: nil)
completionHandler()
}
}
But unfortunately when I do that the app crashes. I've implemented push notification using firebase. I want the app to run normally like it does it's just like presenting a view controller over the root view controller on launch when user is coming from notification.
UPDATEhere is the screenshot of my storyboard:
So you'll need to insatiate tab bar controller first:
let storyboard = UIStoryboard.init(name: "YourStoryboardName", bundle: nil)
let tabBarController = storyboard.instantiateViewController(withIdentifier: "YourTabBarController") as! UITabBarController
And insatiate all of the UINavigationControllers with the your desired view controller:
let firstNavigationController = storyboard.instantiateViewiController(withIdentifier: "YourFirstNavigationController") as! UINavigationController
let NotificationVC = storyboard.instantiateViewController(withIdentifier: "NotificationVC") as! NotificationsViewController
And make them all as viewControllers of tab bar controller:
tabBarController.viewControllers = [firstNavigationController]
Which is the navigation controller should be present this view controller? For example:
tabBarController.selectedViewController == firstNavigationController {
firstNavigationController.present(NotificationVC, animated: true, completion: nil)
}
It's a last thing:
self.window = UIWindow.init(frame: UIScreen.main.bounds)
self.window?.rootViewController = tabBarController
self.window?.makeKeyAndVisible()
Note It's just recommendation and therefore I have used one UNavigationController
So I have created a function called present().
func present() {
let storyboard = UIStoryboard.init(name: "YourStoryboardName", bundle: nil)
let tabBarController = storyboard.instantiateViewController(withIdentifier: "YourTabBarController") as! UITabBarController
let firstNavigationController = storyboard.instantiateViewiController(withIdentifier: "YourFirstNavigationController") as! UINavigationController
let NotificationVC = storyboard.instantiateViewController(withIdentifier: "NotificationVC") as! NotificationsViewController
tabBarController.viewControllers = [firstNavigationController]
tabBarController.selectedViewController == firstNavigationController {
firstNavigationController.present(NotificationVC, animated: true, completion: nil)
}
self.window = UIWindow.init(frame: UIScreen.main.bounds)
self.window?.rootViewController = tabBarController
self.window?.makeKeyAndVisible()
}
}
But it's not useful even I think this is useless. We'll need an action whenever notification is tapped. And we should check an action identifier like this:
extension AppDelegate: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
switch response.actionIdentifier {
case UNNotificationDefaultActionIdentifier:
self.present()
completionHandler()
default:
break;
}
}
}
Hope it helps
Maybe your rootView Controller is nil. Try setting a rootview_Controller before presenting class even though you may have specified a rootviewController in did finish launnch of appDelegate.
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void) {
viewController = storyboard.instantiateViewControllerWithIdentifier("YourRootVC") as UIViewController
self.window?.rootViewController = viewController
window?.rootViewController?.present(yourPusHNotificationVC, animated: true, completion: nil)
completionHandler()
}
}

Why status bar disappear when I tap push notification and move ViewController in swift

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
/* */
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = UIViewController()
self.window?.windowLevel = UIWindowLevelAlert + 1
self.window?.makeKeyAndVisible()
/*
fetch and add push notification data
*/
goAnotherVC()
}
func goAnotherVC() {
if (application.applicationState == UIApplicationState.active) {
/* active stage is working */
} else if (application.applicationState == UIApplicationState.inactive || application.applicationState == UIApplicationState.background) {
if (type == "1" || type == "2") {
let storyboard: UIStoryboard = UIStoryboard(name: "MyAppointments", bundle: nil)
let apptVC = storyboard.instantiateViewController(withIdentifier: "NotificationDetailViewController") as! NotificationDetailViewController
let navigationController = UINavigationController.init(rootViewController: apptVC)
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()
} else if (type == "3") {
let storyboard: UIStoryboard = UIStoryboard(name: "MyAppointments", bundle: nil)
let apptVC = storyboard.instantiateViewController(withIdentifier: "NotificationDetailViewController") as! NotificationDetailViewController
let navigationController = UINavigationController.init(rootViewController: apptVC)
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()
} else if (type == "4") {
let storyboard: UIStoryboard = UIStoryboard(name: "Enquiry", bundle: nil)
let enqVC = storyboard.instantiateViewController(withIdentifier: "EnquiryDetailViewController") as! EnquiryDetailViewController
let navigationController = UINavigationController.init(rootViewController: enqVC)
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()
}
}
}
Above code is fetch push notification data and send to related View Controller when user tapped on it. But I've found that status bar is missing when View Controller is open. Please let me know how to fix it, thanks.
At firs glance (and without actually verifying the code) my suspicion would be that the culprit is this:
self.window = UIWindow(frame: UIScreen.main.bounds)
You are setting your window to cover the full bounds of the view. To see if this is the issue, simply change that line to the following:
var rect = UIScreen.main.bounds
rect.origin.y += 20
self.window = UIWindow(frame: rect)
And see if the issue goes away. In my code, I've simply changed the top position of the window to allow for the status bar.

Resources