Managing Location Services

The location of a user’s device is sensitive information, and that’s why our app needs to get permission from users to access it. In this section, we’ll create a class that manages the access to and handling of the location data.

In Xcode, create a new file using the shortcut N, select the Cocoa Touch Class template, and click Next. Type LocationProvider in the Class text field and make it a subclass of NSObject. Click Next and then click Create.

Asking for Permission

Our app will access the location services via an instance of CLLocationManager, which is defined in CoreLocation. Import CoreLocation below the existing import statement:

 import​ ​UIKit
 import​ ​CoreLocation

This import statement makes the classes in the CoreLocation framework accessible in this file. Next, replace the LocationProvider class with the following code:

 class​ ​LocationProvider​: ​NSObject​, ​CLLocationManagerDelegate​ {
 
 private​ ​let​ locationManager: ​CLLocationManager
 
 
 override​ ​init​() {
  locationManager = ​CLLocationManager​()
 
 super​.​init​()
 
  locationManager.delegate = ​self
  locationManager.distanceFilter = 1
  locationManager.​requestWhenInUseAuthorization​()
  }
 }

This code adds a property for the location manager to LocationProvider and assigns it to a new instance of CLLocationManager in the initializer. As LocationProvider is a subclass of NSObject, we need to call super.init() before we can access the instance of LocationProvider via self. This is one of the rules of initialization in Swift. You can learn more about these rules in the official documentation provided by Apple.[4]

After the super.init call, we assign self to the delegate property of the locationManager. The locationManager will notify its delegate about changes concerning the location services, such as when the authorization is changed or a new location is detected. Our LocationProvider class can act as the delegate of the location manager only when it conforms to the CLLocationManagerDelegate protocol. This is why we have added CLLocationManagerDelegate to the class declaration.

Next we set the distance filter to one meter, which means the locationManager will notify the delegate only when the device has moved at least that far. This is a good way to reduce the amount of data we have to deal with. Finally, we ask for permission to access the user’s location when the app is in use.

Before we move on, let’s import our LogStore library below the existing import statements:

 import​ ​UIKit
 import​ ​CoreLocation
 import​ ​LogStore

The locationManager notifies its delegate when the user has responded to the permission request. Add the following method to the end of LocationProvider:

 func​ ​locationManager​(_ manager: ​CLLocationManager​,
  didChangeAuthorization status:
 CLAuthorizationStatus​) {
 
 switch​ status {
 case​ .authorizedWhenInUse:
 printLog​(​"success"​)
 case​ .denied:
 printLog​(​"denied"​)
 // you will implement this case
 // in the exercises
 default​:
 break
  }
 }

This code just prints the word success to the debug output if the user selects Allow in the authorization dialog. You will implement the other cases in the exercises for this chapter.

Before we move on with implementing LocationProvider, let’s check whether asking for permission works. Add the highlighted lines in the following code to ViewController:

 class​ ​ViewController​: ​UIViewController​ {
 
»var​ locationProvider: ​LocationProvider​?
 
 override​ ​func​ ​viewDidLoad​() {
 super​.​viewDidLoad​()
 
  view.backgroundColor = .red
 
» locationProvider = ​LocationProvider​()
  }
 }

In this code we add a property of type LocationProvider to the view controller and initialize it in viewDidLoad. This should trigger the authorization request as soon as the view of the view controller has loaded.

Build and run the app on the simulator. You won’t see the authorization alert in the simulator. To figure out what’s going on, open the debug console by selecting View > Debug Area > Activate Console. You should see something like this:

 This app has attempted to access privacy-sensitive data without
 a usage description. The app's Info.plist must contain an
 "NSLocationWhenInUseUsageDescription" key with a string value
 explaining to the user how the app uses this data

Errors with exact descriptions of what to do to fix them are the best! Let’s do what the error suggests. Open the Info.plist and add an entry with the key NSLocationWhenInUseUsageDescription and the value “Your location is used to show your path on a map.” When you’re done, it should look like this:

images/Map/location_usage_description_info_plist.png

As you can see in the screenshot, Xcode does not show the key NSLocationWhenInUseUsageDescription but rather expands it to a more human-readable form: “Privacy - Location When In Use Usage Description.”

Build and run the app again. After the app launches, iOS presents the location permission alert with three choices: Allow While Using App, Allow Once, and Don’t Allow.

If the user selects Allow While Using App, our app can access the location of the device whenever it is running. If the user selects Allow Once, our app can access the location during this session and UIKit will ask for permission again the next time the app is launched. If the user selects Don’t Allow, UIKit denies access to the device location.

Once a user has denied access to the location, the app can’t ask again and the location manager always delivers the status denied. In this case, the app could show an alert that it cannot function properly and ask if the user wants to change the permission in the app settings. We could even redirect the user to the settings with the following code:

 let​ settingsURL = ​URL​(string: ​UIApplication​.openSettingsURLString)!
 UIApplication​.shared.​open​(settingsURL)

In the running simulator, select Allow While Using App, and the word success gets printed to the debug console.

Pro Tip: Use a Real Description

images/aside-icons/tip.png

Enter a real description of the app’s usage right from the beginning. Even though you may be tempted to skip this step because you’re eager to see the feature running, it’s likely that you’ll forget to improve the usage description before you submit the first version of the app to the App Store for review. Sit down for a few minutes and think about how users will feel when they read the description.

If you’d like to trigger the permission alert again during development, open the Settings app in the simulator, navigate to General > Reset, and select Reset Location & Privacy.

Toggle Tracking

To start tracking the user’s location, we need to call startUpdatingLocation on the locationManager. Add the following method in LocationProvider above the method locationManager(_:didChangeAuthorization:):

 func​ ​start​() {
  locationManager.​startUpdatingLocation​()
 }

This way, we abstract the locationManager away from the parts that are interested only in the location updates. Callers need to know only that they can start location updates, not who is going to deliver these updates.

Whenever there is an update for the location of the device, the locationManager calls the locationManager(_:didUpdateLocations:) of its delegate. To see this in action, add the following method to the end of LocationProvider:

 func​ ​locationManager​(_ manager: ​CLLocationManager​,
  didUpdateLocations locations: [​CLLocation​]) {
 printLog​(​"locations: ​​(​locations​)​​"​)
 }

For testing purposes, we just print the locations delivered by the locationManager to the debug console. To start the tracking, add the following method to the class ViewController.

 override​ ​func​ ​viewWillAppear​(_ animated: ​Bool​) {
 super​.​viewWillAppear​(animated)
 
  locationProvider?.​start​()
 }

UIKit calls viewWillAppear(_:) when the view is about to be presented to the user. This is the perfect moment to start doing tasks like gathering location updates.

Build and run the app, and open the debug console. Then select the simulator menu item Features > Location > Custom Location... to simulate different locations. In the debug console you should see location updates pouring in when you change the simulated location. Great! We managed to track the user’s movement.

If you don’t see the location updates in the debug console, review the steps and compare them with the code you wrote.

The next step is to create the user interface.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.216.34.146