NSNotification: Attempt to present UIAlertController on ViewController whose view is not in the window hierarchy

Viewed 795

I'm trying to show a UIAlertController in my ViewController in a function that's been called via an NSNotification. However I'm getting the error:

Attempt to present <UIAlertController: 0x7fe013d05d40> on <submarine.ViewController: 0x7fe011f20370> whose view is not in the window hierarchy!

The NSNotification is posted from a completion block (callback I guess) from something else in my UI. Because it's a callback it's failing to display. Hence I thought I'd try NSNotificationCentre to get around the problem without using the rootViewController to display the alert.

My code is:

override func viewDidAppear(animated: Bool) {
    // Handle onboarding
    if needsOnboarding() {
        handleOnboarding() // This create the completion block that posts the NSNotification
    }

    NSNotificationCenter.defaultCenter().addObserver(self, selector: "showTermsAlert:", name:"showTermsAlert", object: nil)
}

func showTermsAlert(notification: NSNotification) {
    let termsAlert:UIAlertController = UIAlertController(title: "Terms And Conditions", message: "Please view the terms below before accepting them.", preferredStyle: UIAlertControllerStyle.Alert)

    termsAlert.addAction(UIAlertAction(title: "View Terms", style: .Default, handler: { (action: UIAlertAction!) in
        UIApplication.sharedApplication().openURL(NSURL(string: "my_terms_url")!)
    }))

    termsAlert.addAction(UIAlertAction(title: "I Agree to the Terms", style: .Default, handler: { (action: UIAlertAction!) in
        self.onboardingFinished()
    }))

    self.presentViewController(termsAlert, animated: true, completion: nil)
}

Has anyone got an idea why this is happening? I don't see why it's not in the window hierarchy - it's being presented from the self viewController and is created in a top-level function inside the VC.

Thanks!

EDIT: original code inside the handleOnboarding():

Library used: Onboard

func handleOnboarding() {

        let secondPage = OnboardingContentViewController(title: "What's going on?", body: "Submarine routes your data through our network, around any filters and restrictions, giving you unrestricted and unmonitored internet access.", image: UIImage(named: "back"), buttonText: "Next") { () -> Void in
            // do something here when users press the button, like ask for location services permissions, register for push notifications, connect to social media, or finish the onboarding process
        }
        secondPage.movesToNextViewController = true

        let thirdPage = OnboardingContentViewController(title: "Terms of Use", body: "You must agree to our Terms of Use to use Submarine.\nIf you don't, please close Submarine.", image: UIImage(named: "back"), buttonText: "View Terms") { () -> Void in

            let termsAlert:UIAlertController = UIAlertController(title: "Terms And Conditions", message: "Please view the terms below before accepting them.", preferredStyle: UIAlertControllerStyle.Alert)

            termsAlert.addAction(UIAlertAction(title: "View Terms", style: .Default, handler: { (action: UIAlertAction!) in
                UIApplication.sharedApplication().openURL(NSURL(string: "my_policy_url")!)
            }))

            termsAlert.addAction(UIAlertAction(title: "I Agree to the Terms", style: .Default, handler: { (action: UIAlertAction!) in
                self.onboardingFinished()
            }))

            self.presentViewController(termsAlert, animated: true, completion: nil)

//            NSNotificationCenter.defaultCenter().postNotificationName("showTermsAlert", object: nil)
        }

        // Image
        let onboardingVC = OnboardingViewController(backgroundImage: UIImage(named: "back"), contents: [secondPage, thirdPage])

        self.navigationController?.presentViewController(onboardingVC, animated: false, completion: nil)

    }
2 Answers
Related