[SWIFT] I tried to summarize the points to consider when acquiring location information with the iOS application ①

This time, I would like to summarize the points that must be taken into consideration when acquiring location information with the iOS app.

Introduction

I'm currently developing an app that searches for restaurants from my current location.

Of course, you have to get your current location because it is an app that searches for restaurants from your current location. Then, it was not as simple as getting your current location and saying "Yes, it's over!".

First of all, I would like to write from the minimum necessary procedure to obtain the current location.

procedure

To get location information on iOS app

  1. Permission to obtain privacy information
  2. Import the CoreLocation framework and initialize the CLLocationManager class
  3. Request permission from the user to use location services 4.Adopt CLLocationManagerDelegate protocol and implement delegate method

Roughly speaking, you can get location information by following the procedure like this. Then, I will explain step by step.

1. Permission to obtain privacy information

For iOS apps, permission is required when retrieving privacy-related information. Location information is also privacy-related information, so you cannot use it without permission before using the location information service.

To get permission, use info.plist! Let's show the manifestation of intention. Key:Location When In Use Usage Description Value: Arbitrary string (eg this app uses location services)

In my case it looks like this ↓ スクリーンショット 2021-01-08 11.43.11.png

2. Import the framework, initialize the class

First, import the Core Location framework so that location-related services can be used within the app.

ViewController


import CoreLocation

It then initializes the CLLocationManager class included in the CoreLocation framework. This class manages the location information function.

Use this class as a member property (property defined within the class) Declare it in ViewController and initialize the instance.

ViewController


import CoreLocation

final class ViewController: UIViewController {
 //Declare an instance locationManager of the CLLocationManager class as a member property
 var locationManager: CLLocationManager = {
  //Initialize the instance
  var locationManager = CLLocationManager()
  //Acquisition accuracy setting(Accuracy within 1km)
  locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
  //Location information acquisition interval(Get location information after moving 5m)
  locationManager.distanceFilter = 5
  return locationManager
 }()
}

In the CLLocationManager class, you can adjust various settings such as location information acquisition accuracy and location information acquisition interval. It's the desiredAccuracy and distanceFilter properties that are written at the same time as the declaration.

There is an advantage that the battery lasts a long time by setting it according to your application. Location-based services use a lot of electricity, so you have to consider these points as well.

3. Request permission from the user

When using location services, you must request permission from the user. This time, I want to request permission while using the app, so I use the method requestWhenInUseAuthorization () of the CLLocationManager class.

ViewController


import CoreLocation

final class ViewController: UIViewController {
 //Declare an instance locationManager of the CLLocationManager class as a member property
 private var locationManager: CLLocationManager = {
  //Initialize the instance
  var locationManager = CLLocationManager()
  //Acquisition accuracy setting(Accuracy within 1km)
  locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
  //Location information acquisition interval(Get location information after moving 5m)
  locationManager.distanceFilter = 5
  return locationManager
 }()

 override func viewDidLoad() {
    super.viewDidLoad()
    //Request permission from user
    locationManager.requestWhenInUseAuthorization()
  }
}

I would like to build it here.

In the first place, if the location information service of the terminal is off, an alert like this will be displayed. By selecting Settings, the screen will change to the Settings app, so tap it to turn on the location information service On.

If you turn on the location service and then start the app again, you will see an alert like this. This alert is displayed when you call requestWhenInUseAuthorization ().

There are options such as Allow only once and Allow while using the app. This is the location-based service authentication status and there are several types.

The authentication status is summarized in CLAuthorizationStatus. The first time you launch the app, it will be not Determined.

So, at present, the built app is in the not Determined state.

I tried to summarize the authentication status for easy understanding ↓

Authentication status meaning
authorizedAlways Always allow
authorizedWhenInUse Allow while using the app(Includes only one permit)
denied not allowed
restricted Terminal location services are not allowed
notDetermined You have not selected whether you can use location services

By the way, requestWhenInUseAuthorization () is not called other than NotDetermined. This seems to prevent the alert from being displayed even though the request is allowed.

It's okay to request the user, but I can't get the location information yet.

4. Implementation of delegate method

We will implement the delegate method in compliance with the CLLocationManagerDelegate protocol.

ViewController


import CoreLocation

final class ViewController: UIViewController {
 //Declare an instance locationManager of the CLLocationManager class as a member property
 private var locationManager: CLLocationManager = {
  //Initialize the instance
  var locationManager = CLLocationManager()
  //Acquisition accuracy setting(Accuracy within 1km)
  locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
  //Location information acquisition interval(Get location information after moving 5m)
  locationManager.distanceFilter = 5
  return locationManager
 }()

 override func viewDidLoad() {
    super.viewDidLoad()
    //Request permission from user
    locationManager.requestWhenInUseAuthorization()
    //Delegate to your own view controller
    locationManager.delegate = self
  }
}

//Compliant with CLLocationManagerDelegate protocol for view controller
extention ViewController: CLLocationManagerDelegate {
    //Instance initialization of CLLocationManager class and method called when authentication status changes
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        //App's current authentication status
        let status = manager.authorizationStatus
        
        switch status {
        case .authorizedAlways, .authorizedWhenInUse:
            //Start acquisition of location information
            manager.startUpdatingLocation()
            
        case .notDetermined:
            //Request permission from user
            manager.requestWhenInUseAuthorization()
            
        case .denied:
            break
            
        case .restricted:
            break
            
        default:
            break
        }
    }
  //Method called when location information is acquired / updated
  func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        //Get location information
        guard let gps = manager.location?.coordinate else {
            //If you can not get it, display an alert etc.
            return
        }
        //Stop getting location information
        manager.stopUpdatingLocation()
        //Output longitude and latitude
        let lat = gps.latitude
        let lng = gps.longitude
        print("longitude:\(String(describing: lat)),latitude:\(String(describing: lng))")
    }
}

Once the authentication status is determined by requestWhenInUseAuthorization () The locationManagerDidChangeAuthorization delegate method is called and will be processed within that method.

For the time being, what to do when the user selects .authorizedAlways, .authorizedWhenInUse Describes the process when .notDetermined is selected.

You must call the startUpdatingLocation () method to get the location information. This method will start location information acquisition.

Then, after getting the location information, the locationManager (_: didUpdateLocations :) delegate method is called, You can finally get the location information here.

Well, it's been a long time, but the main subject is from here.

Main subject

As mentioned in the title, there are some points to consider when acquiring location information. The first is whether or not there is a location information service for the terminal.

If the location service of the terminal is turned off, the alert "Please turn on the location service" is displayed. I think it will be displayed, but only first launch only after installing the app is displayed. So, if the location service of the terminal is off at the second and subsequent startups, nothing happens. Of course, you can't get location information either.

So what should I do?

Check for location-based services on your device

You have to write the code to check the location information service of the terminal by yourself.

But it's not that difficult, and the CLLocationManager class has some useful methods. It is locationServicesEnabled ().

This method returns a Bool value that indicates whether location services are enabled on the terminal. If true, location-based service on, if false, location-based service off

Now let's incorporate this method into the source code.

And I want to actually display the alert I would like to display alerts in the way of articles I posted to Qiita in the past ↓ Implement UIAlertController with separate files

ViewController


import CoreLocation

final class ViewController: UIViewController {
 //Declare an instance locationManager of the CLLocationManager class as a member property
 private var locationManager: CLLocationManager = {
  //Initialize the instance
  var locationManager = CLLocationManager()
  //Acquisition accuracy setting(Accuracy within 1km)
  locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
  //Location information acquisition interval(Get location information after moving 5m)
  locationManager.distanceFilter = 5
  return locationManager
 }()

 override func viewDidLoad() {
    super.viewDidLoad()
    //Request permission from user
    locationManager.requestWhenInUseAuthorization()
    //Delegate to your own ViewController class
    locationManager.delegate = self
  }
}

//Compliant with CLLocationManagerDelegate protocol for view controller
extention ViewController: CLLocationManagerDelegate {
    //Instance initialization of CLLocationManager class and method called when authentication status changes
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        //When the location information service of the terminal is on
        if CLLocationManager.locationServicesEnabled() {
        //App's current authentication status
        let status = manager.authorizationStatus
        
        switch status {
        case .authorizedAlways, .authorizedWhenInUse:
            //Start acquisition of location information
            manager.startUpdatingLocation()
            
        case .notDetermined:
            //Request permission from user
            manager.requestWhenInUseAuthorization()
            
        case .denied:
            break
            
        case .restricted:
            break
            
        default:
            break
        }
    //When the location information service of the terminal is off
    }else {
       Alert.okAlert(vc: self, title: "Location-based services\n Please turn it on", message: "You can turn it on from the "Settings" app ⇒ "Privacy" ⇒ "Location services"")
    }
 }
  //Method called when location information is acquired / updated
  func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        //Get location information
        guard let gps = manager.location?.coordinate else {
            //If you can not get it, display an alert etc.
            return
        }
        //Stop getting location information
        manager.stopUpdatingLocation()
        //Output longitude and latitude
        let lat = gps.latitude
        let lng = gps.longitude
        print("longitude:\(String(describing: lat)),latitude:\(String(describing: lng))")
    }
}

Branching within func locationManagerDidChangeAuthorization (_ manager: CLLocationManager).

When I tried to build the location service with off, it was displayed properly. Anyway, the app now checks for device location services.

This settles down one case! I think, but we have to assume further from here and implement it. For example, let's say the user selects OK in the alert and returns to the app with location services off. What do you think will happen?

The flow is like this ↓ Location services off> App launch> Alert display (select OK)> Open settings app> Return without doing anything> ???

At this rate, the app `does not check anything``.

This solution can be solved by checking Location Services when the app returns from the background state. Then, I thought about how to do it, but since the article has become long, I will write it at a later date.

Thank you for reading this far! If not here! If you have any questions, please feel free to comment.

The source code up to this point is also posted.

Source code

ViewController


import CoreLocation

final class ViewController: UIViewController {
 private var locationManager: CLLocationManager = {
  var locationManager = CLLocationManager()
  locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
  locationManager.distanceFilter = 5
  return locationManager
 }()

 override func viewDidLoad() {
    super.viewDidLoad()
    locationManager.requestWhenInUseAuthorization()
    locationManager.delegate = self
  }
}

extention ViewController: CLLocationManagerDelegate {
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        if CLLocationManager.locationServicesEnabled() {
        let status = manager.authorizationStatus
        
        switch status {
        case .authorizedAlways, .authorizedWhenInUse:
            manager.startUpdatingLocation()
            
        case .notDetermined:
            manager.requestWhenInUseAuthorization()
            
        case .denied:
            break
            
        case .restricted:
            break
            
        default:
            break
        }
    }else {
       Alert.okAlert(vc: self, title: "Location-based services\n Please turn it on", message: "You can turn it on from the "Settings" app ⇒ "Privacy" ⇒ "Location services"")
    }
 }

 func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let gps = manager.location?.coordinate else {
            return
        }
        manager.stopUpdatingLocation()
        let lat = gps.latitude
        let lng = gps.longitude
        print("longitude:\(String(describing: lat)),latitude:\(String(describing: lng))")
    }
}

Alert


import UIKit

final class Alert {
    static func okAlert(vc: UIViewController,title: String, message: String, handler: ((UIAlertAction) -> Void)? = nil) {
        let okAlertVC = UIAlertController(title: title, message: message, preferredStyle: .alert)
        okAlertVC.addAction(UIAlertAction(title: "OK", style: .default, handler: handler))
        vc.present(okAlertVC, animated: true, completion: nil)
    }
}

Recommended Posts

I tried to summarize the points to consider when acquiring location information with the iOS application ③
I tried to summarize the points to consider when acquiring location information with the iOS application ①
I tried to summarize the points to consider when acquiring location information with the iOS application ②
I tried to summarize the stumbling points when developing an Android application
I tried to summarize iOS 14 support
I tried to summarize the methods used
I tried to summarize the Stream API
[iOS] I tried to make a processing application like Instagram with Swift
I tried to summarize the key points of gRPC design and development
I tried to manage login information with JMX
[JavaScript] The strongest case when I tried to summarize the parts I do not understand
I tried to summarize the state transition of docker
I tried to modernize a Java EE application with OpenShift.
I tried to increase the processing speed with spiritual engineering
I tried to summarize the basics of kotlin and java
I tried to summarize the basic grammar of Ruby briefly
I tried upgrading from CentOS 6.5 to CentOS 7 with the upgrade tool
When I tried to scroll automatically with JScrollBar, the event handler was drawn only once.
I tried to summarize the words that I often see in docker-compose.yml
I tried to summarize what was asked at the site-java edition-
I tried to build the environment of PlantUML Server with Docker
[Ruby] Tonight, I tried to summarize the loop processing [times, break ...]
I tried to implement the image preview function with Rails / jQuery
Special Lecture on Multi-Scale Simulation: I tried to summarize the 5th
I tried to interact with Java
I tried to explain the method
I tried to translate the error message when executing Eclipse (Java)
[Rails] I tried deleting the application
Special Lecture on Multi-Scale Simulation: I tried to summarize the 8th
I tried to make an Android application with MVC now (Java)
I tried to check the operation of gRPC server with grpcurl
I tried to summarize the methods of Java String and StringBuilder
I tried to summarize Java learning (1)
I tried to summarize Java 8 now
Special Lecture on Multi-Scale Simulation: I tried to summarize the 7th
I tried to figure out the flow when performing image analysis with Vision Framework and Core ML
[IOS] What to do when the image is filled with one color
I summarized the points to note when using resources and resources in combination
Problems I was addicted to when building the digdag environment with docker
[Ruby] I tried to diet the if statement code with the ternary operator
I tried to build a Firebase application development environment with Docker in 2020
[Introduction to Java] I tried to summarize the knowledge that I think is essential
I tried to solve the tribonacci sequence problem in Ruby, with recursion.
I tried to visualize the access of Lambda → Athena with AWS X-Ray
[After learning Progate] I tried to summarize form_with while comparing with form_tag
[Ruby] I tried to summarize the methods that frequently appear in paiza
[Ruby] I tried to summarize the methods that frequently appear in paiza ②
I tried to measure and compare the speed of GraalVM with JMH
I tried to publish the reflex measurement application on the Google Play store
I tried to summarize Java lambda expressions
I tried to get started with WebAssembly
I tried to implement the Iterator pattern
What is Docker? I tried to summarize
I tried to implement ModanShogi with Kinx
Since the du command used when the capacity is full is difficult to use, I tried wrapping it with ruby
I tried to make a machine learning application with Dash (+ Docker) part3 ~ Practice ~
Points I stumbled upon when creating an Android application [Updated from time to time]
I tried to compare the infrastructure technology of engineers these days with cooking.
I tried to get the distance from the address string to the nearest station with ruby
I tried to summarize again the devise that was difficult at first sight
[Rails] When transitioning to a page with link_to, move to the specified location on the page