Navigating to Specific View Controllers from AppDelegate Methods

1.9k Views Asked by At

At the moment, I have implemented these two methods in my AppDelegate

func application(application: UIApplication, continueUserActivity userActivity: NSUserActivity, restorationHandler: ([AnyObject]?) -> Void) -> Bool

and

func application(app: UIApplication, openURL url: NSURL, options: [String : AnyObject]) -> Bool

The first will get called if the user opens my app with a search result from Spotlight and the second one gets called if my app gets opened from Apple Maps (since it's a routing app).

MY QUESTION IS, WHAT IS THE BEST WAY TO GO TO A SPECIFIC UIViewController FROM APPDELEGATE (independent from no matter what view the user is in)?

The reason I ask is because at the moment I'm trying to navigate to it manually depending where the user may be. For example, they may be in a UIViewController that is displayed modally (which then needs to be dismissed) or they may be deep in a UINavigationController, in which the app will then need to call popToRootViewController.

Doing it this way, the code is getting hairy and doesn't seem to work right. It also just doesn't seem right to do it this way either because it is very fragile.

2

There are 2 best solutions below

0
On

I think the good place for renavigation is an event UIApplicationDidBecomeActiveNotification with subscription in root view controller (whatever you use).

When you do your code in AppDelegate - just schedule an Action: assemble your parcel with abstract number of properties, data parameters and so on. Store it somewhere (NSUserDefauils - is good place to be, but it may be even SqlCipher instance). And keep rest with hoping to following event.

When UIApplicationDidBecomeActiveNotification is fired - wake up, catch stored Action parcell, and perform your renavigation according to Action properties.

About your modal view controllers. They (exactly - controllers WHO show) should be ready to dismiss all VC's they show modally when renavigation event arrive.

0
On

I just was trying to figure out the how to implement the first method you mentioned and found a helpful start from https://www.hackingwithswift.com/read/32/4/how-to-add-core-spotlight-to-index-your-app-content His code is:

func application(application: UIApplication, continueUserActivity userActivity: NSUserActivity, restorationHandler: ([AnyObject]?) -> Void) -> Bool {
    if userActivity.activityType == CSSearchableItemActionType {
        if let uniqueIdentifier = userActivity.userInfo?[CSSearchableItemActivityIdentifier] as? String {
            let splitViewController = self.window!.rootViewController as! UISplitViewController
            let navigationController = splitViewController.viewControllers[splitViewController.viewControllers.count-1] as! UINavigationController

            if let masterVC = navigationController.topViewController as? MasterViewController {
                masterVC.showTutorial(Int(uniqueIdentifier)!)
            }
        }
    }

    return true
}

I found self.window?.rootViewController as? yourRootViewControllerClass a good springboard for your question.

My code, which was very basic, looks like this:

// create a storyBoard item to instantiate a viewController from. If you have multiple storyboards, use the appropriate one. I just have one so it is "Main"
let sb = UIStoryboard(name: "Main", bundle: nil)

// get the navigation controller from the window and instantiate the viewcontroller I need.
if let viewController = sb.instantiateViewControllerWithIdentifier("DetailViewController") as? ViewController,
let nav = window?.rootViewController as? UINavigationController {

    viewController.setupController(bookName)// setup the viewController however you need to. I have a method that I use (I grabbed the bookName variable from the userActivity)
    nav.pushViewController(viewController, animated: false)//use the navigation controller to present this view.

} 

This works for me but I must give a caveat. My app has only one storyboard with three viewControllers (NavController->tableViewController->ViewController). I am not sure how this logic will work on more complex apps.

Another good reference is: http://www.appcoda.com/core-spotlight-framework/

func application(application: UIApplication, continueUserActivity userActivity: NSUserActivity, restorationHandler: ([AnyObject]?) -> Void) -> Bool {
    let viewController = (window?.rootViewController as! UINavigationController).viewControllers[0] as! ViewController
viewController.restoreUserActivityState(userActivity)

    return true
}

That viewController has this method:

override func restoreUserActivityState(activity: NSUserActivity) {
    if activity.activityType == CSSearchableItemActionType {
        if let userInfo = activity.userInfo {
            let selectedMovie = userInfo[CSSearchableItemActivityIdentifier] as! String
            selectedMovieIndex = Int(selectedMovie.componentsSeparatedByString(".").last!)
            performSegueWithIdentifier("idSegueShowMovieDetails", sender: self)
        }
    }
}