How to open tabbar BY click notification - ios

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
}
}
}

Related

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

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]?
}

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
}

Unable to navigate to specific viewController after clicking push notification in foreground using FCM below iOS 10

I am trying to get the notification in foreground as well as in background, i am getting notification in both states, but i'm unable to navigate it to desired view controller after tapping the notification when app is in foreground, Any help will be highly appreciated, thanks in advance
i am using third party for custom banner, running on iphone 4s device, ios 9
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
// If you are receiving a notification message while your app is in the background,
// this callback will not be fired till the user taps on the notification launching the application.
// TODO: Handle data of notification
// With swizzling disabled you must let Messaging know about the message, for Analytics
Messaging.messaging().appDidReceiveMessage(userInfo)
print("In did Recieve Notification")
// Print message ID.
print("userInfoNotification=\(userInfo)")
if let contactID = userInfo["contactID"] as? String {
self.contactID = contactID
print(contactID)
}
let state = UIApplication.shared.applicationState
if state == .active {
print("App in Foreground")
if self.contactID != AppDelegate.openedChatContactId {
print("openedID=\(AppDelegate.openedChatContactId)")
if let aps = userInfo["aps"] as? NSDictionary {
if let alert = aps["alert"] as? NSDictionary {
let body = alert["body"] as! String
let title = alert["title"] as! String
let banner = Banner(title: title, subtitle: body, image: UIImage(named: "AppIcon"), backgroundColor: UIColor(red:31.00/255.0, green:136.0/255.0, blue:254.5/255.0, alpha:1.000))
banner.dismissesOnTap = true
banner.show(duration: 3.0)
// let storyboard = UIStoryboard(name: "Main", bundle: nil)
// let viewController = storyboard.instantiateViewController(withIdentifier: "chatMessageVC") as! ChatMessagesVC
// UIApplication.shared.keyWindow?.rootViewController = viewController;
} else if let alert = aps["alert"] as? NSString {
}
}
}
}
if state == .inactive || state == .background {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
var destinationViewController = storyboard.instantiateViewController(withIdentifier: "chatMessageVC") as! ChatMessagesVC
UserDefaults.standard.set(contactID, forKey: "contactID")
UserDefaults.standard.synchronize()
destinationViewController.contactID = self.contactID
let navigationController = self.window?.rootViewController as! UINavigationController
navigationController.pushViewController(destinationViewController, animated: false)
}
}
Implement below extension of UIApplication
extension UIApplication {
class func topViewController(controller: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
if let navigationController = controller as? UINavigationController {
return topViewController(controller: navigationController.visibleViewController)
}
if let tabController = controller as? UITabBarController {
if let selected = tabController.selectedViewController {
return topViewController(controller: selected)
}
}
if let presented = controller?.presentedViewController {
return topViewController(controller: presented)
}
return controller
}
}
Please change your code to navigate to specific screen while app is in foreground.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier: "chatMessageVC") as! ChatMessagesVC
UIApplication.topViewController()?.navigationController?.pushViewController(viewController, animated: false)
Hope this finds you well and let me know in case of any queries.
UPDATE
If the application is running in the foreground, iOS won't show a notification banner/alert. That's by design. But we can achieve it by using UILocalNotification as follows
if application.applicationState == .active {
var localNotification = UILocalNotification()
localNotification.userInfo = userInfo
localNotification.soundName = UILocalNotificationDefaultSoundName
localNotification.alertBody = message
localNotification.fireDate = Date()
UIApplication.shared.scheduleLocalNotification(localNotification)
}

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()
}
}

3D touch quick actions preview view controller only one time

I have the following issue. When I run my app on my phone, 3d touch the icon and select the quick action it launches the app presenting the correct view controller but when I put the app in background and try to invoke the quick action it just opens the app where I have left it. So to make it work I have to kill my app every time.
Here is my code:
func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem, completionHandler: (Bool) -> Void) {
if shortcutItem.type == "com.traning.Search" {
let sb = UIStoryboard(name: "Main", bundle: nil)
let searchVC = sb.instantiateViewControllerWithIdentifier("searchVC") as! UINavigationController
let root = UIApplication.sharedApplication().keyWindow?.rootViewController
root?.presentViewController(searchVC, animated: false, completion: { () -> Void in
completionHandler(true)
})
}
}
Thanks in advance.
I'm guessing you're trying to present a view controller from a view controller that's not visible. You can use extensions like:
extension UIViewController {
func topMostViewController() -> UIViewController {
if self.presentedViewController == nil {
return self
}
if let navigation = self.presentedViewController as? UINavigationController {
return navigation.visibleViewController.topMostViewController()
}
if let tab = self.presentedViewController as? UITabBarController {
if let selectedTab = tab.selectedViewController {
return selectedTab.topMostViewController()
}
return tab.topMostViewController()
}
return self.presentedViewController!.topMostViewController()
}
}
extension UIApplication {
func topMostViewController() -> UIViewController? {
return self.keyWindow?.rootViewController?.topMostViewController()
}
}
You can place both of these in your app delegate.swift, above your app delegate class, to get the currently visible view controller. Then present the search view controller on that. For example:
func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem, completionHandler: (Bool) -> Void) {
if shortcutItem.type == "com.traning.Search" {
let sb = UIStoryboard(name: "Main", bundle: nil)
let searchVC = sb.instantiateViewControllerWithIdentifier("searchVC") as! UINavigationController
let topViewController = UIApplication.sharedApplication.topMostViewController()
topViewController.presentViewController(searchVC, animated: false, completion: { () -> Void in
completionHandler(true)
})
}
}

Resources