1

Basically, I am trying to implement Home Screen Quick Actions into my app. When I tap on one of the quick actions I get the error:

Warning: Attempt to present theViewController on ViewController whose view is not in the window hierarchy!

I have looked at some other Stack Over Flow posts that had the same issue, but the solutions didn't work for me. Also, in my applicationDidEnterBackground method I added the self.window?.rootViewController?.dismissViewControllerAnimated(true, completion: nil).

Here are some of the relevant 3D Touch methods that I have included in the App Delegate:

func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem, completionHandler: (Bool) -> Void) {
    let handledShortcutItem = self.handleShortuctItem(shortcutItem)
    completionHandler(handledShortcutItem)
}

Also, I have these helper methods:

    enum ShortcutIdentifier: String {
    case First
    case Second

    init?(fullType: String) {
        guard let last = fullType.componentsSeparatedByString(".").last else { return nil }
        self.init(rawValue: last)
    }
    var type: String {
        return NSBundle.mainBundle().bundleIdentifier! + ".\(self.rawValue)"
    }
}

func handleShortuctItem(shortcutItem: UIApplicationShortcutItem) -> Bool {
    var handled = false

    guard ShortcutIdentifier(fullType: shortcutItem.type) != nil else { return false }
    guard let shortcutType = shortcutItem.type as String? else { return false }

    switch(shortcutType) {
    case ShortcutIdentifier.First.type:
        handled = true
        let navVC = mainStoryboard.instantiateViewControllerWithIdentifier("firstViewController") as! FirstViewController
        self.window?.rootViewController?.presentViewController(navVC, animated: true, completion: nil)
        break
    case ShortcutIdentifier.Second.type:
        handled = true
        let navVC = mainStoryboard.instantiateViewControllerWithIdentifier("secondViewController") as! SecondViewController
        self.window?.rootViewController?.presentViewController(navVC, animated: true, completion: nil)
        break
    default:
        break
    }


    return handled
}
Harish
  • 1,289
  • 13
  • 36
  • @matt I edited my post to show some of the code. Any ideas? – Harish Jan 16 '16 at 02:42
  • This might help: http://useyourloaf.com/blog/adding-3d-touch-quick-actions.html The problem could be that the interface has not appeared yet at the time you are running your shortcut. – matt Jan 16 '16 at 03:13
  • @matt Ok, but with 3D Touch I need it to work always because I don't know when the user will tap on the quick action, and I need it to work whenever they do. – Harish Jan 16 '16 at 12:11
  • But the article explains about that. – matt Jan 16 '16 at 12:54
  • @matt Ok, I will check it out as soon as I get home. – Harish Jan 16 '16 at 12:55
  • @matt Thanks a lot for your help. I tried doing what the tutorial said, but I didn't have any luck I am still getting the same error. I am really kind of stuck with this issue... – Harish Jan 17 '16 at 21:47

1 Answers1

1

A sort of mindless solution is to wrap a delay around your presentViewController calls (delay is defined here: dispatch_after - GCD in swift?):

switch(shortcutType) {
case ShortcutIdentifier.First.type:
    handled = true
    let navVC = mainStoryboard.instantiateViewControllerWithIdentifier("firstViewController") as! FirstViewController
    delay(0.3) {
        self.window?.rootViewController?.presentViewController(navVC, animated: true, completion: nil)
    }
    break
case ShortcutIdentifier.Second.type:
    handled = true
    let navVC = mainStoryboard.instantiateViewControllerWithIdentifier("secondViewController") as! SecondViewController
    delay(0.3) {
        self.window?.rootViewController?.presentViewController(navVC, animated: true, completion: nil)
    }
    break
default:
    break
}

The idea is to give the interface time to finish appearing before trying to do the presentation.

Community
  • 1
  • 1
matt
  • 447,615
  • 74
  • 748
  • 977
  • Hmm... I tried pasting in the code that you put, but it is still not working. I really wonder why this error is happening. I have looked at other stack over flow posts to but nothing seems to be working. I know that the app works fine when I completely close out the app and then do the home screen quick action. However, if the app runs in the background and then I call the quick action I get the error. – Harish Jan 17 '16 at 22:08
  • Actually, I tried your solution, but instead I changed the time from 0.3 to 0.7 seconds which helped me out. I think it just took longer for that view to finish appearing and fully load. Thanks so much for your help, I was really wondering why this wasn't working. – Harish Jan 17 '16 at 22:16
  • But please note that this is not a real solution. It's a proof-of-concept workaround. We're just kicking the can down the road. We know the problem (your code is running before the interface has been assembled) but we have not actually built a solution into the app. – matt Jan 18 '16 at 00:40
  • Yes, and actually I figured out that this solution was working for me because at the time I was calling the 3D Touch quick action there was a view that was a small introduction, and that was scheduled to disappear after 0.5 seconds. So at the time I was calling the quick action it was trying to segue into another view which caused a bunch of errors. The delay function however caused the app to wait for the segue to be complete and then it would go to the correct view once the new view was loaded properly. – Harish Jan 18 '16 at 00:47
  • Excellent, thanks for reporting back on that! – matt Jan 18 '16 at 01:24
  • No problem! Thanks for the help. – Harish Jan 18 '16 at 18:13
  • @matt In my case, which is very similar, this change does not make a difference. This error does not occur for me every time, but only when the app was already open. If I clear the app history and try it then, then it works normally. Any suggestions? Thanks! – GJZ Mar 27 '16 at 18:45