How to check if user clicks no on NSLocationAlwaysAndWhenInUseUsageDescription Swift 3.2 - ios

When user is on MainViewController
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.requestAlwaysAuthorization()
locationManager.requestWhenInUseAuthorization()
}
viewDidLoad() will run and the alert will appear
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Accessing user location to determine the nearest pickup location</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Accessing user location to determine the nearest pickup location</string>
The main question is, if user clicks Don't allow, how do I check programmtically? for example
// Example code snippet, not from apple officials
if NSlocation is nil then I want to do some alert

If the user chooses to deny your app access to location services, CLLocationManager.authorizationStatus() will be .denied (though you should also watch out for .restricted. If you'd like to be notified when the user chooses whether or not to allow or deny your app, you can make yourself your location manager's delegate and implement the proper method:
class MainViewController: CLLocationManagerDelegate{
let locationManager = CLLocationManager()
override func viewDidLoad() -> Void{
super.viewDidLoad()
locationManager.delegate = self
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) -> Void{
switch status{
case .authorizedAlways, .authorizedWhenInUse:
// You're allowed to use location services
case .denied, .restricted:
// You can't use location services
default:
// This case shouldn't be hit, but is necessary to prevent a compiler error
}
}
}

Use below delegate method of CLLocationManager to check user click on Don't allow button in permission alert.
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
switch status {
case .denied:
print("Show you own alert here")
break
}
}
To redirect user in location settings follow below:
Add URL Types "prefs" like below image
Write this code on button action:
UIApplication.sharedApplication().openURL(NSURL(string:"prefs:root=LOCATION_SERVICES")!)

Related

Update location when user allows location tracking

I'm trying to animate where the user is when they accepts location tracking. The Code works when they already have accepted location tracking and then loads the view but I would like to have the view reload when they press accept on location tracking.
override func viewDidLoad() {
super.viewDidLoad()
//Prepare to get User
if CLLocationManager.authorizationStatus() != .authorizedAlways {
// May need to change to support location based notifications
locationManager.requestAlwaysAuthorization()
print("hello")
mapView.setVisibleMapRect(mapView.visibleMapRect, animated: true)
}else {
locationManager.startUpdatingLocation()
}
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
mapView.delegate = self
}
Animated to user location Code
//Get user location
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
//Show the map at the users current location
let location = locations[0]
let span:MKCoordinateSpan = MKCoordinateSpanMake(0.02,0.02 )
let myLocation:CLLocationCoordinate2D = CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude)
let region:MKCoordinateRegion = MKCoordinateRegionMake(myLocation, span)
mapView.setRegion(region, animated: true)
self.mapView.showsUserLocation = true
locationManager.stopUpdatingLocation()
}
You can use:
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
switch status {
case .notDetermined:
locationManager.requestAlwaysAuthorization()
break
case .authorizedWhenInUse:
locationManager.startUpdatingLocation()
break
case .authorizedAlways:
locationManager.startUpdatingLocation()
break
default:
break
}
}
All methods used to update locations in CLLocationManager are asynchronous, so there might be a delay from the user allowing location access to the location update actually happening regardless of the implementation of your code.
However, by calling CLLocationManager().requestLocation() inside locationManager(didChangeAuthorizationStatus) you can ensure that you receive a location update as close as possible to the acceptance of location usage.
func locationManager(manager: CLLocationManager!, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
switch status {
case .notDetermined:
locationManager.requestAlwaysAuthorization()
case .authorizedWhenInUse:
locationManager.requestLocation()
case .authorizedAlways:
locationManager.requestLocation()
default:
break
}
}
This will automatically call your CLLocationManagerDelegate method as soon as the asynchronous requestLocation function finishes execution.

swift - didUpdateLocations Not Being Called

I'm working on an app that requires getting the user's current coordinates. I was planning on doing this through CLLocationManager's didUpdateLocations method. For some reason, didUpdateLocations is not being executed. However, it appears that locationManager.startUpdatingLocation() is being called successfully. None of the other possible solutions I've seen on this site have worked for me. I already added NSLocationAlwaysUsage to info.plist. Here is the entirety of my code:
import UIKit
import MapKit
import CoreLocation
var region: MKCoordinateRegion!
class ViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate {
#IBOutlet weak var map: MKMapView!
var locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
self.locationManager.requestAlwaysAuthorization()
self.locationManager.requestWhenInUseAuthorization()
switch CLLocationManager.authorizationStatus() {
case .authorizedWhenInUse, .authorizedAlways:
if CLLocationManager.locationServicesEnabled() {
locationManager.startUpdatingLocation()
print("Updating location now")
}
case .notDetermined:
locationManager.requestAlwaysAuthorization()
case .restricted, .denied:
print("User must enable access in settings")
break
}
if (region == nil){
}
else {
map.setRegion(region!, animated: true)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("Got location")
let userLocation:CLLocation = locations[0]
let lat:CLLocationDegrees = userLocation.coordinate.latitude
let long:CLLocationDegrees = userLocation.coordinate.longitude
let currentPos:CLLocationCoordinate2D = CLLocationCoordinate2DMake(lat, long)
didUpdateRegion(position: currentPos)
print(lat)
print(long)
}
func didUpdateRegion(position: CLLocationCoordinate2D) {
let span = MKCoordinateSpanMake(0.075, 0.075)
region = MKCoordinateRegion(center: position, span: span)
}
func locationManager(_manager: CLLocationManager, didFailWithError error: Error) {
print(error.localizedDescription)
}
func locationManager(manager: CLLocationManager!, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
switch status {
case .notDetermined:
// If status has not yet been determied, ask for authorization
manager.requestWhenInUseAuthorization()
break
case .authorizedWhenInUse:
// If authorized when in use
manager.startUpdatingLocation()
break
case .authorizedAlways:
// If always authorized
manager.startUpdatingLocation()
break
case .restricted:
print("User must activate location services in settings")
break
case .denied:
print("User must activate location services in settings")
break
default:
break
}
}
When I run this code on both the simulator and an actual device, I get the notification to allow location tracking. After accepting that, the console displays "Updating location now," but never gets to printing "Got location." Thank you for any light you can shed on this issue, I'm new to app development in general.
EDIT: I added in the entirety of my code instead of just the parts I thought were relevant. Basically, I'm trying to get the region shown on the map to follow the user. I attempt to do this by updating the variable "region" every time the didUpdateLocations function fires.
Am I getting it right and you only added one key - NSLocationAlwaysUsage?
Try to add both keys to the Info.plist:
Privacy - Location When In Use Usage Description
Privacy - Location Always Usage Description
Also, what happens if you implement this method of protocol?
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("\(error.localizedDescription)")
}
Does it print anything? Sorry, I was going to leave a comment, but I don't have enough reputation.

Prompt user to allow location services after being denied without crashing

The problem I am facing is that when I press a UIButton - location services are required to initiate the action. However if the user was to deny Location Services at the initial launch of the app - the app will crash.
I have tried finding a way to implement CLAuthorizationStatus .Denied but I can't seem to find a way to do so. The only code I can seem to implement is the didChangeAuthorizationStatus which only initiates the request at First Launch of the application.
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus)
{
if status == .AuthorizedAlways || status == .AuthorizedWhenInUse
{
manager.startUpdatingLocation()
}
else
{
manager.requestWhenInUseAuthorization()
}
}
If I press the UIButton to send the API Request the app will crash if location services have been denied.
My question is how can I implement a method, within the button's IBAction, that will direct the user to go to their settings and enable location services. :)
CLLocationManager has a static function authorizationStatus() that you can use to get the current authorization status without even initializing a CLLocationManager object.
So in the function that you call when the user presses the button you can check the authorization status and act accordingly:
import UIKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
lazy var locationManager = CLLocationManager()
...
func didPressButton(sender: UIButton) {
switch CLLocationManager.authorizationStatus() {
case .AuthorizedAlways, .AuthorizedWhenInUse:
locationManager.delegate = self
locationManager.startUpdatingLocation()
case .NotDetermined:
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
case .Denied:
print("Show Alert with link to settings")
case .Restricted:
// Nothing you can do, app cannot use location services
break
}
}
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
if status == .AuthorizedWhenInUse {
manager.startUpdatingLocation()
}
}
}

Swift background fetch location updates with Core Location

I want to use Background Fetch Location Updates service on my app. But don't show any output my codes here i want your help.
import CoreLocation
class ViewController: UIViewController, UITextFieldDelegate, CLLocationManagerDelegate {
var locationManager:CLLocationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
self.locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
}
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
print("didChangeAuthorizationStatus")
switch status {
case .NotDetermined:
print(".NotDetermined")
break
case .Authorized:
print(".Authorized")
self.locationManager.startUpdatingLocation()
break
case .Denied:
print(".Denied")
break
default:
print("Unhandled authorization status")
break
}
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations.last as CLLocation!
print("didUpdateLocations: \(location.coordinate.latitude), \(location.coordinate.longitude)")
}
Giving output only
didChangeAuthorizationStatus
.NotDetermined
If it possible i want to take long lat value when new location changes.
Thank you !
I added this to the info.plist file:
<key>NSLocationAlwaysUsageDescription</key>
<string>Your message goes here</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Your message goes here</string>

Swift LocationManager didChangeAuthorizationStatus Always Called

I have a view controller which implements the CLLocationManagerDelegate. I create a the CLLocationManager variable:
let locationManager = CLLocationManager()
Then in the viewDidLoad, I set properties:
// Set location manager properties
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters
locationManager.distanceFilter = 50
The problem comes that the function gets called even before I check the authorization status.
func locationManager(manager: CLLocationManager!, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
if (status == .AuthorizedWhenInUse) {
// User has granted autorization to location, get location
locationManager.startUpdatingLocation()
}
}
Can anyone inform me what could be causing this to occur?
- locationManager:didChangeAuthorizationStatus: is called shortly after the CLLocationManager is initialised.
You can request authorization inside the delegate method if you want:
func locationManager(manager: CLLocationManager!, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
switch status {
case .notDetermined:
locationManager.requestAlwaysAuthorization()
break
case .authorizedWhenInUse:
locationManager.startUpdatingLocation()
break
case .authorizedAlways:
locationManager.startUpdatingLocation()
break
case .restricted:
// restricted by e.g. parental controls. User can't enable Location Services
break
case .denied:
// user denied your app access to Location Services, but can grant access from Settings.app
break
default:
break
}
}
Be aware that you need to assign the delegate in a 'timely' matter if you want this to work.
If you would somehow delay the delegate assignment, e.g. by setting it asynchronously, you might miss the initial call to - locationManager:didChangeAuthorizationStatus:.
Swift 3
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
switch status {
case .notDetermined:
manager.requestAlwaysAuthorization()
break
case .authorizedWhenInUse:
manager.startUpdatingLocation()
break
case .authorizedAlways:
manager.startUpdatingLocation()
break
case .restricted:
// restricted by e.g. parental controls. User can't enable Location Services
break
case .denied:
// user denied your app access to Location Services, but can grant access from Settings.app
break
}
}
The other answers could introduce new undesirable behaviors.
You can just add a boolean and a guard to prevent the first call, with some comments explaining the bug :
var firstTimeCalled = true
// ...
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
guard !firstTimeCalled else {
firstTimeCalled = false
return
}
// ... send status to listeners
}

Resources