This time, I would like to summarize the points that must be taken into consideration when acquiring location information with the iOS app.
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.
To get location information on iOS app
Roughly speaking, you can get location information by following the procedure like this. Then, I will explain step by step.
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 ↓
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.
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.
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.
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?
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.
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