I'm trying to request location "Always" but no matter my settings I only see three options:
Allow While Using App
Allow Once
Don't Allow
Here is my code:
// ViewController.swift
import UIKit
import CoreLocation
class ViewController: UIViewController {
var locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
locationManager.allowsBackgroundLocationUpdates = true
locationManager.pausesLocationUpdatesAutomatically = false
}
}
My Info.plist includes strings for these three:
Privacy - Location Always and When In Use Usage Description
Privacy - Location When In Use Usage Description
My app Capabilities has "location updates" turned on in Background Modes.
What am I missing here?
Edit: Removed an old Info.plist key/value pair that was deprecated.
iOS 13 Location Permissions
NSLocationAlwaysUsageDescription - deprecated.
This key is required if your iOS app uses APIs that access the user’s location at all times and deploys to targets earlier than iOS 11.
The Always Allow option, which grants an app “background” location
access, has been removed from the initial location permissions prompt.
Instead, users can select Keep Only While Using (aka “foreground”
permission) or select a new option,Allow Once. So how does an app then
get upgraded to “background” location permission? If the user
continues to use the app, iOS 13 will now automatically and
periodically prompt to upgrade location permissions from While Using
to Always Allow.
Related
I would like to requests the user’s permission to use location services while using the app, Allow Once or Don't allow.
enter image description here
I only have code in ViewController.swift because I build the app with Ruby on Rails.
Which property list key I should use and how?
You need to call locationManager for this:
locationManager.requestAlwaysAuthorization()
To request "always" location access as well as:
locationManager.requestWhenInUseAuthorization()
I am currently testing the background location mode in iOS 13, as I want to track the user´s location and motion (using the CMMotionManager) in the background.
Therefore, I have my own (singleton) class handling the location tracking. I initialize the CLLocationManager in the following way:
func initializeLocationManager() -> CLLocationManager {
let manager = locationManager ?? CLLocationManager()
manager.delegate = self
manager.requestAlwaysAuthorization()
manager.allowsBackgroundLocationUpdates = true
manager.pausesLocationUpdatesAutomatically = false
manager.distanceFilter = kCLDistanceFilterNone
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.activityType = .other
return manager
}
Then I start the following services:
func startLocationServices() {
// ...
locationManager.startUpdatingLocation()
locationManager.startMonitoringVisits()
locationManager.startMonitoringSignificantLocationChanges()
// ...
}
In addition, I implemented the CLLocationManagerDelegate-methods didChangeAuthorization(), didUpdateLocation().
In the info.plist-file I appended the following entries:
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>...</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>...</string>
<key>UIBackgroundModes</key>
<array>
<string>location</string>
</array>
In my ViewController, I call the startLocationServices.
Currently, I set the app's authorization to track location data to ".authorizedAlways"
The location updates stop after approximately 60 - 130 minutes.
To solve the problem, I already added the didFinishLaunchingWithOptions-function, which triggers the location-updates again:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
if let launchOptions = launchOptions,
let isLocationKey = launchOptions[UIApplication.LaunchOptionsKey.location] as? Bool,
isLocationKey {
restartServices()
}
return true
}
When the app gets awoken using this function, I managed to get continuous data on some tests, but sometimes the app was suspended again after a few minutes.
Last, I also tried a timer that restarts the location tracking every 5 minutes, but this does not seem to affect the update-duration at all.
So my question is if there is a way of continuously receiving the location updates in the background, or is there some option I am missing?
Thanks in advance.
Edit: I tested the application on iOS 12 and it gets continuous updates in 5/5 tests.
So I guess the problem is related to iOS 13.
Nothing is wrong with code !! ,
I have faced the same issue and after research I found that
At the WWDC19 keynote Apple announced two changes in the way location permissions will work in iOS 13. The first change gives users the option to share their location with your app just once. This makes it easier to try out location features and helps users keep sensitive location data private.
The first notable change is that even when you call requestAlwaysAuthorization, the user will only get the ‘just now’ and ‘when in use’ options in the permission dialog. If the user grants you ‘when in use’ permission and you try to scan for location in the background, only then the user will be presented a dialog to grant the background permission.
So When user grants WhenInUseUsage permission You will get always in CLAuthorizationStatus and If user choose Allow Once CLAuthorizationStatus will be rested to notDetermined once app launches again
You can check this article for detailed info
https://medium.com/q42-engineering/apple-location-permission-ios13-1e0e59002889
And Here is video https://developer.apple.com/videos/play/wwdc2019/705/
EDIT
After When In User permission granted by the user iOS will show user a another dialogue after some days to change permission from when in use to always allow.
So now there is no way to directly ask user for always allow permission instantly like we do before.
This is an iOS 13 bug. The app permissions get messed up by iOS. While it shows "Always" in settings, it actually behaves more like "While in Use". One app is put to sleep in background while other keeps getting locations on the same device (in background).
To solve it, goto Settings-> Search your app with issue -> Location -> Change Always to Never and then back to Always.
Unfortunately, there is nothing, you can do in the code to fix this.
Core functionality of my app is updating location data in background mode. In iOS 13, when we calling locationManager.requestAlwaysAuthorization(), system asks user to choose among variants 'Never', 'Permit Once' and 'When in use'.
If user grants 'When in use' access, our app will be able to work only in foreground.
The thing i can't understand is following:
Sometimes when app goes into background and after a while became active and goes into background again, iOS 13 asks user to change location access to 'Always'
What should my app to do to make iOS 13 to show this dialog to user? (I want to do it, when my app goes into background at first time)
P.S. I know, i can use some custom alert with text like "please, go to system settings and adjust location access for this app to 'Always' mode". But i need to know, is there any way to use "native system flow" as described above?
Thanks!
#Claudio's answer helps me to solve my problem. I've found that it is able to access location in background having 'When in use' permission. To do so, you must set locationManager.showsBackgroundLocationIndicator = true.
Here is my locationManager adjustment:
let locationManager = CLLocationManager()
if #available(iOS 9, *){
locationManager.allowsBackgroundLocationUpdates = true
}
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.headingFilter = kCLHeadingFilterNone
locationManager.pausesLocationUpdatesAutomatically = true
locationManager.activityType = .otherNavigation
if #available(iOS 11.0, *) {
locationManager.showsBackgroundLocationIndicator = true;
}
locationManager.delegate = self
locationManager.startMonitoringSignificantLocationChanges()
This is the new iOS 13 behaviour. This has been implemented by apple to give users more transparency. For a detailed understanding of this change you can refer to this link
I believe that's the new iOS 13 behaviour, here it is stated that
If your app requests and receives When In Use authorization, you can
make a separate request for Always authorization later. However, apps
may make only one request for Always authorization.
Also for reference you can check this thread
I have followed the apple developer guide on location services.
I have included the info.plist key value pair:
key: Privacy - Location Always Usage Description
value: The application myTestApp needs access to location services even in the background
I have created an instance of CLLocationManager as a class variable of the view controller:
var locationManager = CLLocationManager()
I have code in the view controller viewDidLoad() function that assigns the delegate and checks for the current status:
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
let status = CLLocationManager.authorizationStatus()
switch status {
case .denied, .restricted, .notDetermined, .authorizedWhenInUse :
print("The switch detected a state other than always")
locationManager.requestAlwaysAuthorization()
default:
print("Services Authorized")
}
Yet, when I build and run the application after making small changes it does not prompt for user access, and even worse, when it does sometimes work it thinks the user is in the middle of the Atlantic ocean at Lat: 0.0 and Long: 0.0.
Is there anything outside of the code I need to do so that I can recreate the user experience of authorizing the application and then seeing it zoom to a real location?
The user permission prompt is one time only. Once it is allowed,
device would fetch user location automatically from that point.
Try setting the custom location in the simulator via these two methods.
1. Using the simulator menu actions
2. Using the Xcode debugger options
The simulator handles location a bit differently from the device. I'd suggest pushing the app over to the device itself and running from there to see how it asks for permission and make sure it is giving the right location. It's very easy to do and very helpful for testing with apps that use access to location services.
https://developer.apple.com/library/content/documentation/IDEs/Conceptual/AppDistributionGuide/LaunchingYourApponDevices/LaunchingYourApponDevices.html
Essentially, plug phone in, select it from the list of devices to simulate with, press play and accept your developer access on the phone to run the app from your device.
In android, you define permissions for gps, sms sending, location , .., in the manifest file.
Is there anything similar in the iOS, so the user would know what capabilities of the phone some app uses before installation?
Or is the user warned during app use when some function wants to use something (e.g. gps, sms...)?
In iOS you declare your application requirements in its manifest-like Info.plist. But this information is not used to ask user permission, only for ensuring device compatibility.
Only Notifications and Location Services require user permission, which is automatically asked to the user the very first time your application attempt to use the corresponding API.
My guess is that many other permissions are already granted via the Apple Store license agreement, that the user must have accepted, unlike Android (I guess you can install an app without using the market isnt? which changes a lot from a legal point of view)
There's no such things as permissions on iPhone.
The only thing that user is warned about is when application uses his current location - then user is prompted with system alert and must explicitly allow or deny application's access to location data.
What concerns sms and email, they can be created and sent only via standard controllers so user will be aware of that anyway
on iOS you don't have to declare all necessary permissions in some specific file. You just use them. For example location
info.plist
//Privacy - Location When In Use Usage Description
<key>NSLocationUsageDescription</key>
<string>title</string>
//Privacy - Location Always and When In Use Usage Description
<key>NSLocationWhenInUseUsageDescription</key>
<string>description</string>
import CoreLocation
let locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.requestAlwaysAuthorization()
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
}
[IDFA]