swiftios93dtouchuiapplicationshortcutitem

Navigating to a ViewController from AppDelegate triggered by UIApplicationShortcutItem


In my application, the first view that is launched is controlled by RootUIViewController. Now the user can tap on any of the buttons on this view and then segue to a view controlled by LeafUIViewController. Each button points to this same view, with different value for some of the arguments.

Now I have implemented the 3D Touch shortcut menu which correctly calls the following function in the AppDelegate:

func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem, completionHandler: (Bool) -> Void)

In this function, I want to go to the LeafUIViewController such that the user can still navigate back to the RootViewController.

What is the right way to do this so that the Root is correctly instantiated, pushed on stack and then view navigates to the Leaf?


Solution

  • I suggest against doing any launch actions specific segues from that callback. I usually set a global state and handle it in all my relevant viewcontrollers. They usually pop to the main viewcontroller. In there I do programatically do the action just as would the user normally do.

    The advantage is that the viewcontroller hierarchy is initialised in a normal way. The state can be then handled by the first viewcontroller that's going to be displayed on screen which isn't necessarily the first viewcontroller of the app. The application could be already initialised when the user triggers the action. So there could be a random viewcontroller in the hierarchy.

    I use something like:

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        if #available(iOS 9.0, *) {
            if let shortcutItem = launchOptions?[UIApplicationLaunchOptionsShortcutItemKey] as? UIApplicationShortcutItem {
                handleShortcutItem(shortcutItem)
            }
        }
        return true
    }
    
    enum ShortcutType: String {
        case myAction1 = "myAction1"
        case myAction2 = "myAction2"
    }
    
    @available(iOS 9.0, *)
    func handleShortcutItem(shortcutItem: UIApplicationShortcutItem) -> Bool {
    
        if let shortcutType = ShortcutType.init(rawValue: shortcutItem.type) {
    
            switch shortcutType {
                case .myAction1:
                    MyHelper.sharedInstance().actionMode = 1
                    return true
                case .myAction2:
                    MyHelper.sharedInstance().actionMode = 2
                    return true
                default:  
                   return false
            }
        }
        return false
    }
    

    and then in main viewController (such as main menu) handle the action somehow:

    override func viewDidAppear() {
        super.viewDidAppear()
        switch MyHelper.sharedInstance().actionMode {
                case 1:
                    // react on 1 somehow - such as segue to vc1
                case 2:
                    // react on 2 somehow - such as segue to vc2
            default:  
                break
        }
        // clear the mode
        MyHelper.sharedInstance().actionMode = 0
    }
    

    And in other vc's:

    override func viewDidLoad() {
        super viewDidLoad()
        NSNotificationCenter.defaultCenter().addObserver(self, selector: "reloadView", name: UIApplicationWillEnterForegroundNotification, object: nil)
    }
    
    func reloadView() {
        if MyHelper.sharedInstance().actionMode {
            self.navigationController.popToRootViewControllerAnimated(false) 
        }
    }
    

    This might not be the best if you are using several vc's. If there is something better, I'love to learn that :)