Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

iOS Displaying Data with Table Views in Swift 2 Obtaining a Location Fix Adding a Location Manager

Keep getting 'Attempting to present UIAlertController whose view is not in the window hierarchy!'

Below is my code. I'm in a split view controller using navigation controllers to present views. I'm just trying to present an alert view controller modally for when the user declines location services. Below is my code how do I proceed?

import Foundation
import CoreLocation
import UIKit

final class LocationManager: NSObject, CLLocationManagerDelegate {

    let manager = CLLocationManager()

     override init() {
        super.init()
        manager.delegate = self
    }

    func getPermission() {
        switch CLLocationManager.authorizationStatus() {
        case .AuthorizedWhenInUse: manager.startUpdatingLocation()
        case .AuthorizedAlways:  manager.startUpdatingLocation()
        case .NotDetermined: manager.requestWhenInUseAuthorization()
        case .Restricted, .Denied:
            let alertController = UIAlertController(
                title: "Background Location Access Disabled", message: "To view restaurants, Open settings and set location acccess to 'Always', or 'When in Use'.",
                preferredStyle: .Alert)

            let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel, handler: nil)
            alertController.addAction(cancelAction)

            let openAction = UIAlertAction(title: "Open Settings", style: .Default) { (action) in
                if let url = NSURL(string: UIApplicationOpenSettingsURLString) {
                    UIApplication.sharedApplication().openURL(url)
                }
            }
            alertController.addAction(openAction)
            let splitViewController = UIApplication.sharedApplication().windows[0].rootViewController as! UISplitViewController
            let activeViewController = splitViewController.viewControllers[splitViewController.viewControllers.count-1] as! UINavigationController
            activeViewController.topViewController!.presentViewController(alertController, animated: true, completion: nil)

        }

    }
}

1 Answer

Greg Kaleka
Greg Kaleka
39,021 Points

Hi Damion,

If you're calling this method from a view controller, I would suggest modifying the method to take the sender as a parameter. If you do, you'll skip all the back bending to figure out which view should do the presenting.

func getPermission(sendingVC: UIViewController) {
// other stuff //
alertController.addAction(openAction)
sendingVC.presentViewController(alertController, animated: true, completion: nil)

Not only is this a lot cleaner, but it's much more versatile. If you want to have a different VC asking for permission, all you have to do is call the method from somewhere else, rather than figuring out how to get that different VC to present the alert. This is a clear win by separating concerns. The location manager shouldn't be worried about which UIViewController presents the alert.

You may even want to take this a step further and create a new class that handles the alert stuff, as that really shouldn't be the concern of a location manager either... This is a good first step, though.