How to handle push notification click in iOS Swift? - ios

I'm trying to handle push notification click. When push notification comes in, I want to direct the user to a certain part of the application after clicking on push notification. Firstly, there is SplashScreen in the application, where it shows a TabBar as "Present Modally" by checking whether the user has previously logged in from API Call. What I want to show the user is the content of a feed in the table view in the navigation controller in the first tab of the TabBar. But I can't reach it and show it as clicked on tableview.
-SplashScreen -------> TabBar -> Navigation Controller -> Table View Controller -> ContentView(I want to show)
I tried writing the UIApplication extension but it didn't work.
Here's the extension
extension UIApplication {
class func topViewController(base: UIViewController? = (UIApplication.shared.delegate as! AppDelegate).window?.rootViewController) -> UIViewController? {
if let nav = base as? UINavigationController {
return topViewController(base: nav.visibleViewController)
}
if let tab = base as? UITabBarController {
if let selected = tab.selectedViewController {
return topViewController(base: selected)
}
}
if let presented = base?.presentedViewController {
return topViewController(base: presented)
}
return base
}
}
I wrote the following code in didFinishLaunchingWithOptions
if let option = launchOptions {
let info = option[UIApplication.LaunchOptionsKey.remoteNotification] as? [String:Any]
if let info = info {
if info["type"] as! String == "site-notification" {
let contentVC = Destination().FeedCoontentVC
contentVC.selectedSite = (info["link"] as! String)
contentVC.title = "asd"
UIApplication.topViewController()?.present(contentVC, animated: true, completion: nil)
}
}
}

if info["type"] as! String == "site-notification" {
let tabbarSB = UIStoryboard(name: StoryBoard.tabBar, bundle: nil)
let tabbarVC = tabbarSB.instantiateViewController(withIdentifier: ViewController.TabBar) as! TabBarController
self.window?.rootViewController = tabbarVC
tabbarVC.viewControllers = [] // add your tab view controllers
for child in (tabbarVC.childViewControllers) {
if child.restorationIdentifier == "tablevc" //add restoration id to storyboard {
tabbarVC.selectedIndex = 1
let tableVC = (child.childViewControllers[0]) as! TableViewController
let contentSB = UIStoryboard(name: StoryBoard.contentSB , bundle: nil)
let contentVC = contentSB.instantiateViewController(withIdentifier: ViewController.contentVC ) as! FeedCoontentVC
contentVC.selectedSite = (info["link"] as! String)
contentVC.title = "asd"
tableVC.navigationController?.pushViewController(contentVC, animated: false)
}
}
}

Related

Check to specific VC in AppDelegate

I'm trying to check specific VC if app is running in foreground. My root view controller class is SWRevealViewController. After that I have a TabBarController and under it there is NavigationController and ViewController under it.
My heirachy is,
SWRevealViewController --> TabBar Controller --> Navigation Controller --> MessageVC --> ChatVC
I want to check in app delegate if app is on ChatVC or not if running on foreground.I have tried this code,
let tabBar:UITabBarController = self.window?.rootViewController as! UITabBarController
let navInTab:UINavigationController = tabBar.viewControllers?[1] as! UINavigationController
let storyboard = UIStoryboard(name: "Dashboard", bundle: nil)
let destinationViewController = storyboard.instantiateViewController(withIdentifier: "ChatDetailViewController") as? ChatDetailViewController
if destinationViewController?.restorationIdentifier == "ChatDetailViewController"
{
print("Yes")
}
else
{
print("No")
}
But app crashes with this error,
Could not cast value of type 'SWRevealViewController' (0x100dc4b20) to 'UITabBarController' (0x211b289f0).
How i can check if app is on ChatVC or not?
Screenshot of storyboard :
I have an extension for it
extension UIApplication {
class func topViewController(base: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
if let nav = base as? UINavigationController {
return topViewController(base: nav.visibleViewController)
}
if let tab = base as? UITabBarController {
let moreNavigationController = tab.moreNavigationController
if let top = moreNavigationController.topViewController, top.view.window != nil {
return topViewController(base: top)
} else if let selected = tab.selectedViewController {
return topViewController(base: selected)
}
}
if let presented = base?.presentedViewController {
return topViewController(base: presented)
}
return base
}
}
USAGE:
if UIApplication.topViewController is YourViewController {
// do smth
}
I never used SWRevealViewController but you can try this.
As you said in your first line "SWRevealViewController is the RooVC" and you are converting the SWRevealViewController to UITabBarController (check below line of your code). This is your crash reason.
let tabBar:UITabBarController = self.window?.rootViewController as! UITabBarController
Now you need to change, UITabBarController to SWRevealViewController
let rootVC = self.window?.rootViewController as! SWRevealViewController
Now get all ViewControllers
if let navController = rootVC.navigationController { // for safety check
for controller in navController.viewControllers {
if controller is ChatVC {
print("Chat VC is available")
break
}
}
}
For safe coding and keeping swift optional binding in mind, you can do code like below,
Updated answer
var haveChatVC = false
if let rootVC = self.window?.rootViewController as? SWRevealViewController,
let tabbar = rootVC.frontViewController as? UITabBarController {
if let requiredNC = tabbar.viewControllers?[1] as? UINavigationController {
for vc in requiredNC.viewControllers {
if vc is ChatVC {
// ...
haveChatVC = true
break
}
}
}
else {
print("Navigation controller not found.")
}
}
else {
print("Unable to get root controller or navigation controller")
}
if haveChatVC {
// do task here when chat vc available
}
else {
// do task here when chat vc not available
}
Note: This is only pseudo code.

Show two ViewController from AppDelegate

When APP is Launching - start SigninView - it's Okey. Next if success - I need showTripController(). Function work but nothing show? What's a problem?
func showSigninView() {
let controller = self.window?.rootViewController!.storyboard?.instantiateViewControllerWithIdentifier("DRVAuthorizationViewController")
self.window?.rootViewController!.presentViewController(controller!, animated: true, completion: nil)
}
func showTripController() {
let cv = self.window?.rootViewController!.storyboard?.instantiateViewControllerWithIdentifier("DRVTripTableViewController")
let nc = UINavigationController()
self.window?.rootViewController!.presentViewController(nc, animated:true, completion: nil)
nc.pushViewController(cv!, animated: true);
}
First of all you must add this before you use window :
self.window.makeKeyAndVisible()
Another thing to keep in mind is:
Sometimes keyWindow may have been replaced by window with nil rootViewController (showing UIAlertViews, UIActionSheets on iPhone, etc), in that case you should use UIView's window property.
So, instead of using rootViewController, use the top one presented by it:
extension UIApplication {
class func topViewController(base: UIViewController? = UIApplication.sharedApplication().keyWindow?.rootViewController) -> UIViewController? {
if let nav = base as? UINavigationController {
return topViewController(base: nav.visibleViewController)
}
if let tab = base as? UITabBarController {
if let selected = tab.selectedViewController {
return topViewController(base: selected)
}
}
if let presented = base?.presentedViewController {
return topViewController(base: presented)
}
return base
}
}
if let topController = UIApplication.topViewController() {
topController.presentViewController(vc, animated: true, completion: nil)
}
Replace last 3 lines of showTripController as below:
let nc = UINavigationController(rootViewController: cv));
self.window!.rootViewController = nc

I have multiple storyboards. How can I use AppDelegate to open another ViewController in another storyboard? (Segue)

Here is the code I have. I have tried a few different approaches and some of them gives me the error that the view is not in the hierarchy.
The code snippet below goes in the correct else but can't perform the segue or presentViewController
func applicationDidTimout(notification: NSNotification) {
if let vc = self.window?.rootViewController as? UINavigationController {
if let myTableViewController = vc.visibleViewController as? AccountsOverviewViewController {
// Call a function defined in your view controller.
myTableViewController.signOffUser()
} else {
// We are not on the main view controller. Here, you could segue to the desired class.
let storyboard = UIStoryboard(name: "Accounts", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("AccountsNavigationController") as UIViewController
let vc2 = getVisibleViewController(nil)
vc2?.presentViewController(vc, animated: true, completion: nil)
}
}
}
func getVisibleViewController(var rootViewController: UIViewController?) -> UIViewController? {
if rootViewController == nil {
rootViewController = UIApplication.sharedApplication().keyWindow?.rootViewController
}
if rootViewController?.presentedViewController == nil {
return rootViewController
}
if let presented = rootViewController?.presentedViewController {
if presented.isKindOfClass(UINavigationController) {
let navigationController = presented as! UINavigationController
return navigationController.viewControllers.last!
}
if presented.isKindOfClass(UITabBarController) {
let tabBarController = presented as! UITabBarController
return tabBarController.selectedViewController!
}
return getVisibleViewController(presented)
}
return nil
}
Use the func below to get the visible view controller,
func getVisibleVC() -> UIViewController? {
if var visibleVC = window?.rootViewController {
while let presentedVC = visibleVC.presentedViewController {
visibleVC = presentedVC
}
return visibleVC
}
return nil
}

How to presentViewController embedded in UITabBarController and UINavigationBarController

When using 3D Touch Shortcuts from the home screen I am trying to segue to different view controller.
The application is embedded within a UITabBarController and each tab root controller is a UINavigationController.
Here is how I attempted handling the shortcuts to load the view controller for each shortcut.
private func handleShortcutItem(shortcutItem: UIApplicationShortcutItem) {
if let rootViewController = window?.rootViewController, let shortcutItemType = ShortcutItemType(shortcutItem: shortcutItem) {
let sb = UIStoryboard(name: "main", bundle: nil)
let helloVC = sb.instantiateViewControllerWithIdentifier("HelloVC") as! HelloViewController
let goodbyeVC = sb.instantiateViewControllerWithIdentifier("GoodbyeVC") as! GoodbyeViewController
switch shortcutItemType {
case .Hello:
rootViewController.presentViewController(helloVC, animated: true, completion: nil)
break
case .Goodbye:
rootViewController.presentViewController(goodbyeVC, animated: true, completion: nil)
break
}
}
}
With this code the shortcuts only open the application to the initial view controller and not to the helloVC and goodbyeVC controllers which are in different tabs.
I am presuming this is because the ViewControllers I am trying to load are embedded within a UINavigationController as well as embedded within the UITabBarController.
How can I presentViewController which is embedded within the UITabBarController and UINavigationController ?
UPDATE
I am unsure if the following works as I have not got a iPhone 6S with me atm. But I have changed the code to the following, hopefully this will load the selected tab index when the 3D Touch Action is performed. From there it should post a notification to the view controller to perform a segue.
private func handleShortcutItem(shortcutItem: UIApplicationShortcutItem) {
if let rootViewController = window?.rootViewController, let shortcutItemType = ShortcutItemType(shortcutItem: shortcutItem) {
let tababarController = rootViewController as! UITabBarController
switch shortcutItemType {
case .Hello:
tababarController.selectedIndex = 1
NSNotificationCenter.defaultCenter().postNotificationName("performsegueHello", object: nil)
break
case .Goodbye:
tababarController.selectedIndex = 4
NSNotificationCenter.defaultCenter().postNotificationName("performsegueGoodbye", object: nil)
break
}
}
}
Please try the following code. I think you are not reaching the navigation controller of tab bar.you can do this by :
let insideNvc = tvc?.selectedViewController as? UINavigationController
Now, here is your navigation controller you can present or push anything on it.
private func handleShortcutItem(shortcutItem: UIApplicationShortcutItem) {
let nvc = self.window?.rootViewController as? UINavigationController
let tvc = nvc?.topViewController as? TabBarController
let insideNvc = tvc?.selectedViewController as? UINavigationController
if let shortcutItemType = ShortcutItemType(shortcutItem: shortcutItem) {
let sb = UIStoryboard(name: "main", bundle: nil)
let helloVC = sb.instantiateViewControllerWithIdentifier("HelloVC") as! HelloViewController
let goodbyeVC = sb.instantiateViewControllerWithIdentifier("GoodbyeVC") as! GoodbyeViewController
switch shortcutItemType {
case .Hello:
insideNvc?.presentViewController(helloVC, animated: true, completion: nil)
break
case .Goodbye:
insideNvc?.presentViewController(goodbyeVC, animated: true, completion: nil)
break
}
}
}

How to get top view controller when UIImagePickerController is presented?

At some time I present UIImagePickerViewController. Once it is presented, I call my function: UIStoryboard.topViewController():
extension UIStoryboard {
class func topViewController(base: UIViewController? = UIApplication.sharedApplication().keyWindow?.rootViewController) -> UIViewController? {
if let nav = base as? UINavigationController {
return topViewController(nav.visibleViewController)
}
if let svc = base as? UISplitViewController where svc.viewControllers.count == 1 {
return topViewController(svc.viewControllers[0])
}
if let presented = base?.presentedViewController {
return topViewController(presented)
}
return base
}
}
When I print result, all I get is:
0x000000014cb2aa00
{
UIKit.UIResponder = {...}
}
How to get UIImagePickerController from topViewController() function?
Not a complete answer to your case, but here's how I find the top VC in my program. You should be able to edit it for your case.
class UIHelper {
static func getCurrentViewController() -> UIViewController? {
var currentViewController: UIViewController?
if let window = UIApplication.sharedApplication().delegate?.window {
currentViewController = window!.rootViewController?.presentedViewController
}
if currentViewController == nil {
return nil
}
//Check for my version of my main tab bar VC
if let tabBarController = currentViewController as? RootTabBarController {
currentViewController = tabBarController.selectedViewController
print("Tab bar presents \(currentViewController)")
}
// Check if it's a nav VC
if let navController = currentViewController as? UINavigationController {
currentViewController = navController.viewControllers[0] as? UIViewController
print("Nav controller presents \(currentViewController)")
}
print("Current controller: \(currentViewController)")
return currentViewController
}
}

Resources