SwiftyJD SwiftyJD - 4 months ago 13
iOS Question

How to add a closure to a system method?

I'm currently creating an app that uses CLLocationManager, but instead of having the prompt for asking locations permissions in the app delegate, I have it executed when a button to "check in" is pressed. I'm trying to create a closure that allows the user to check-in after they have accepted to allow location services. As of right now after the user accepts the location services the code that checks them in isn't activated because the check to see if location services is enabled happens before the user actually accepts the location services. Here is my code:

typealias CompletionHandler = (success:Bool) -> Void

func askLocationPermission (completionHandler: CompletionHandler) {

self.locationsManager.requestWhenInUseAuthorization()

}


@IBAction func checkInButtonPressed(sender: AnyObject) {

askLocationPermission { (success) in

if CLLocationManager.locationServicesEnabled() {
self.locationsManager.delegate = self

if let location = self.locationsManager.location {

self.currentUserLatitude = location.coordinate.latitude
self.currentUserLongitude = location.coordinate.longitude

print("This is the current latitide: \(location.coordinate.latitude)")
print("This is the current longitude: \(location.coordinate.longitude)")

self.checkInLocation(userInfo.sharedInstance.getAccessToken(), id: userInfo.sharedInstance.getMemberID()!, latitude: self.currentUserLatitude!, radius: 0.3, longitude: self.currentUserLongitude!)


}

}

}
}

}

Answer

Instead of adding the closure, you can just use the delegate structure that CLLocationManager provides. Here is documentation for CLLocationManager (as well as a larger tutorial on how to use it) and here is documentation for the delegate.

First, when the button is pressed, we want to register as a delegate so we get all updates from the CLLocationManager. This way, the CLLocationManager will tell us when the authorization status changes and when it gets a new location. After we register as a delegate, we check the authorization status. If it hasn't been decided, we ask the user for permission. Otherwise we either don't have access, or the user already gave permission and we just grab their location!

@IBAction func checkInButtonPressed(sender: AnyObject) {

    // Set us as a delegate so the manager updates us
    self.locationsManager.delegate = self

    // Check our authorization status and request access if we need
    if CLLocationManager.authorizationStatus() ==  kCLAuthorizationStatusNotDetermined {
        // User hasn't given permission yet, ask for permission
        self.locationsManager.requestWhenInUseAuthorization()
    } else if CLLocationManager.authorizationStatus() ==  kCLAuthorizationStatusRestricted || CLLocationManager.authorizationStatus() ==  kCLAuthorizationStatusDenied {
        // Handle denied access
    } else {
        // We have access! Start listening now
        self.locationsManager.startUpdatingLocation()
    }
}

Next, we want to handle the scenario where the user hasn't given us access yet, and we present the permission popup to them. They can tap yes (or no) so we want to handle both cases. Just like above, if they tapped yes, just start getting the location!

// Called when the user changes the authorization status- in this case
//  this will change when the user taps yes/no in the permission popup
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
    if CLLocationManager.authorizationStatus() ==  kCLAuthorizationStatusRestricted || CLLocationManager.authorizationStatus() ==  kCLAuthorizationStatusDenied {
        // Handle denied access
    } else {
        // We have access! Start listening now
        self.locationsManager.startUpdatingLocation()
    }
}

Finally, we can start doing stuff! CLLocationManager calls this whenever it gets a new location, so this is the perfect time to check in! It can take a while to get a good lock, so you may want to alert the user in checkInButtonPressed that you're working on it and will update them shortly.

// Called every time the locationManager gets a new location
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {

    self.currentUserLatitude = manager.coordinate.latitude
    self.currentUserLongitude = manager.coordinate.longitude

    print("This is the current latitide: \(location.coordinate.latitude)")
    print("This is the current longitude: \(location.coordinate.longitude)")

    self.checkInLocation(
        userInfo.sharedInstance.getAccessToken(), 
        id: userInfo.sharedInstance.getMemberID()!, 
        latitude: self.currentUserLatitude!, 
        radius: 0.3, 
        longitude: self.currentUserLongitude!)
}  
Comments