Location Services

You will create a simple application which shows how far you are from Chester

  1. Start by creating a new iOS application using the Single View template and have set the language to Swift
  2. In the view add two labels, one that simply says '00000.0', and the other that says 'miles from Chester'.
  3. Connect the '00000.0' label up to the view controller as an outlet named milesLabel
  4. In order to use location services within iOS, permission must be granted by the user, to do this developers must set a configuration option in the Info.plist file containing a message to the end user explaining why access is required

    Open the Info.plist file and click the + sign which appears when you hover the mouse over the words Information Property List at the top
  5. From the drop down list that appears choose 'Privacy - Location Usage Description'. Set the value for this key to be a message informing the user that location services are required for the app to work
  6. Add another entry, and for the key choose Privacy - Location When In Use Usage Description, and for the value enter a message such as "We will only use your location when you have the app open"
  7. In the ViewController class add a statement at the top of the file (above the class declaration)to import the core location framework as follows
    import CoreLocation
  8. At the top of the class add the following code to create a variable to hold the instance of CLLocationManager:
    var locationManager = CLLocationManager()
  9. in the viewDidLoad method, we need to check the authorisation status for location services, which will be one of the following:
    • AuthorizedAlways
    • AuthorizedWhenInUse
    • Denied
    • Restricted
    • NotDetermined

    If it is NotDetermined, then the user has not been asked to allow location monitoring, and we are permitted to prompt them to request access. If it is denied, then we would need to inform the user to allow access in Settings (we cannot prompt again for permission, but we may instead instruct them how to change settings). If the status is Restricted then the user does not have permission to allow access (for example parental controls are enabled), and locations services cannot be enabled. Finally, if we have either of the Authorized statuses then we can start to use location services, furthermore, if the status is AuthorizedAlways, we would be able to carry out location tasks in the background.

    Add the following code to request authorisation when the app is in use:
    if CLLocationManager.authorizationStatus() == .notDetermined {
        locationManager.requestWhenInUseAuthorization()
    }
  10. We now need to configure the location manager object to monitor location - the first thing to do is to set how accurate the location needs to be - the more accurate the location the higher the power consumption, so we should typically choose the lowest acceptable level of accuracy that meets our requirements. Note that BestForNavigation provides a higher level of accuracy then 'Best' but is intended to be user when the device is plugged in (for example when used for in car navigation)

    Add the following code, beneath the if statement, to set the location accuracy to the nearest 100 metres
    locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters
  11. When monitoring location, the location manager provides updates using delegation - the location manager therefore needs to know which class it is delegating to - in our case it will be the view controller. Add a line of code to configure the delegate for the Location manager as follows:
    locationManager.delegate = self
  12. You should notice an error appear, the message stating "Cannot assign value of type 'ViewController' to type 'CLLocationManagerDelegate?'" This is because the ViewController does not conform to the CLLocationManagerDelegate protocol. Modify the ViewController class declaration to adopt the protocol as follows:
    class ViewController: UIViewController, CLLocationManagerDelegate {
  13. Returning to the viewDidLoad method, at the bottom, add the following line to tell the location manager to start updating location:
    locationManager.startUpdatingLocation()
  14. Finally we need to implement a LocationManagerDelegate method to handle updates, add the following code to do that:
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        //get the most recent location
        let lastLocation = locations.last!
        
        //create a location object with the coordinates for Chester
        let chester = CLLocation(latitude: 53.191824, longitude: -2.891015)
        
        //calculate the distance from Chester
        let distanceInMetres = lastLocation.distance(from: chester)
        
        let distanceInMiles = distanceInMetres / 1609.34
        milesLabel.text = String(format: "%.1f", distanceInMiles)
    }
  15. Run the application, modify the location using the Debug → Location menu and check that the distance updates

Further Tasks

  1. The application continually monitors the distance from Chester - it would be useful if once the location was determined location updates were ceased once the location is determined, and a button created to allow users to refresh the location - which should save battery life.
  2. It should be possible to determine which direction someone should travel in (e.g. SouthWest) to reach Chester - provide this information to the user (you may need to research how latitude and longitude work to do this)
  3. Location services can also provide a heading (direction the device is facing) and also its course (direction the device is traveling in). Use one or both of these values to provide further direction to the user in order to reach Chester
  4. The app does not currently handle the potential for the user having denied location access - add code to handle these situations by alerting the user (the alert should inform them to change the app's permissions in Settings)