CLLocation Manager check requestAlwaysAuthorization and if not accept exit app - ios

I have requestAlwaysAuthorization and I need to track users every time if user doesn't accept to requestAlwaysAuthorization I want to do exit in app ?
How can I do it ?
My codes under below.
import CoreLocation
public var locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let altitudeG = locations.last?.altitude
let longitudeG = locations.last?.coordinate.longitude
let latitudeG = locations.last?.coordinate.latitude
print("\(altitudeG) \(longitudeG) \(latitudeG)")
}

In error case this delegatemethod is called:
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
print(error)
// handle not authorized error here. You might want to quit on specific errors (unauthorized) only. Check the error.
UIControl().sendAction(#selector(URLSessionTask.suspend), to: UIApplication.shared, for: nil)
}
You can also check the current permissions state before letting CLLocationManager fail:
if CLLocationManager.locationServicesEnabled() {
switch(CLLocationManager.authorizationStatus()) {
case .notDetermined, .restricted, .denied:
print("No access")
case .authorizedAlways, .authorizedWhenInUse:
print("Access")
}
} else {
print("Location services are not enabled")
}
taken from this answer.
Opinion based: I consider quitting the app instead of giving the user a understandable feedback very bad UX.

Above answer is also good, I just tried to make it bit easy way with methods. Also, if you are working with hardware devices like beacons then you must access the location AuthorizedAlways.
Check if location services are enabled
public func isLocationEnabled()-> Bool {
if CLLocationManager.locationServicesEnabled() {
switch(CLLocationManager.authorizationStatus()) {
case .NotDetermined, .Restricted, .Denied , .AuthorizedWhenInUse :
showLocationServiceNotEnabledAlert()
return false
case .AuthorizedAlways: // As of now we check for only "Always", not for "When In Use" this should be fixed according to the requirements
return true
}
}
return false
}
Alert For User to on the service And redirect to Settings
func showLocationServiceNotEnabledAlert() {
let title = "Your Title"
let message = "Your Message"
let alertController = UIAlertController(title: title, message: message, preferredStyle: .Alert)
let settingsAction = UIAlertAction(title: "Settings".localized, style: .Default) { (alertAction) in
if let appSettings = NSURL(string: UIApplicationOpenSettingsURLString) {
UIApplication.sharedApplication().openURL(appSettings)
}
}
alertController.addAction(settingsAction)
let cancelAction = UIAlertAction(title: "Cancel".localized, style: .Cancel, handler: nil)
alertController.addAction(cancelAction)
UIApplication.sharedApplication().delegate?.window!?.currentViewController?.presentViewController(alertController, animated: true, completion: nil)
}

Related

When using location manager, in xcode/swift, to get a user's current location, I get a value of nil. Any suggestions?

*UPDATE: Now getting location data, and the print statement print("\(currentLocation.coordinate.latitude)") works. If I try to assign what is on the right side of the = to a label.text value, I get an error: unexpectedly found nil while unwrapping an optional value. *
Still new to this whole Swift/Xcode thing. Any help would be greatly appreciated. I'm running the following code (only code relevant to the issue shown). When a user presses a button and selects get location, it is supposed to get their location. Instead, I'm getting nothing. The text in the locationManager never prints and I'm at a loss.
import UIKit
import MapKit
import CoreLocation
import MessageUI
import Photos
import AVFoundation
class ViewController: UIViewController, UITextViewDelegate, UINavigationControllerDelegate, UIImagePickerControllerDelegate, CLLocationManagerDelegate, MFMailComposeViewControllerDelegate {
var locationManager: CLLocationManager()
var currentLocation: CLLocation?
override func viewDidLoad() {
super.viewDidLoad()
locationManager.requestWhenInUseAuthorization()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let currentLocation = locations[0]
print("\(currentLocation.coordinate.latitude)")
issueLocation.text = "\(currentLocation.coordinate.latitude)" ***ERROR***
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Error: \(error)")
}
Stuff that doesn't matter, I hope, and more code
// Attach a location
#IBAction func attachLocation(_ sender: UIButton) {
if issueLocation.text == nil {
issueImage.isHidden = true
}
let alertController = UIAlertController(title: "Location options", message: nil, preferredStyle: .actionSheet)
// Get location
let locationAction = UIAlertAction(title: "Get location", style: .default) { (action) in
self.displayLocation()
}
// Add get location action to alert controller
alertController.addAction(locationAction)
// Remove location
let eraseLocation = UIAlertAction(title: "Erase location", style: .default) { (action) in
self.issueLocation.text == nil
self.issueLocation.isHidden = true
}
// Add erase location to alert controller
alertController.addAction(eraseLocation)
// Cancel action
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (action) in
// Do nothing
}
// Add cancel action to alert controller
alertController.addAction(cancelAction)
// Display alert message on screen
self.present(alertController, animated: true) {
// Code to handle user selection
}
}
func displayLocation() {
let status = CLLocationManager.authorizationStatus()
let noPermissionMessage = "It appears that LoCAL Information does not have access to your location. Click Settings -> LoCAL Information -> Location to allow access to your location."
switch status {
case .notDetermined:
locationManager?.requestWhenInUseAuthorization()
case .authorized, .authorizedAlways, .authorizedWhenInUse:
print("JJJJJ")
print("KKKKK")
issueLocation.isHidden = false
case .denied, .restricted:
self.troubleAlert(message: noPermissionMessage)
#unknown default:
self.troubleAlert(message: noPermissionMessage)
}
}
locationManager is declared but not initialized
Replace
var locationManager: CLLocationManager?
with
let locationManager = CLLocationManager()
and remove the question mark after each occurrence of locationManager

How to prevent iOS app from being suspended

I have an iOS app that needs to track user's geolocation for a research project. However, the app gets automatically suspended by the iOS after a few hours and the geolocation tracking stops working.
Geofencing is not sufficient for my case as it's not accurate enough.
Is there any way to can prevent the app from getting suspended (unless user manually terminate it)?
One way I thought of is to play a silent music indefinitely, and display the music controls on lockscreen through MPRemoteCommandCenter, like how Spotify works.
Would that keep the app alive? (as I believe Spotify never gets killed unless user manually terminate it?)
I have similar app which uses user location for tracking. Check if you have all these permissions in info.plist. And specifically tell users why you are using location permissions
<key>NSLocationAlwaysUsageDescription</key>
<string>Application needs permission to access your current location.
</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Application needs permission to access your current location.
</string>
<key>UIBackgroundModes</key>
<array>
<string>location</string>
</array>
Here is a part of my code. I have removed the unwanted part so you might have to edit while using it.
import GooglePlaces
import GoogleMaps
class StartRideViewController: UIViewController, CLLocationManagerDelegate, GMSMapViewDelegate{
var mapView: GMSMapView!
var locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
let notificationCenter = NotificationCenter.default
notificationCenter.addObserver(self, selector: #selector(appMovedToBackground), name: Notification.Name.UIApplicationWillResignActive, object: nil)
notificationCenter.addObserver(self, selector: #selector(appMovedToForeGround), name: Notification.Name.UIApplicationDidBecomeActive, object: nil)
}
#objc func appMovedToBackground() {
print("App moved to background!")
print(isStartRide)
if isStartRide == false{
btn_Share.isHidden = true
locationManager.allowsBackgroundLocationUpdates = false
locationManager.stopUpdatingLocation()
}
}
#objc func appMovedToForeGround() {
//getMeRidersData()
}
func initiateLocation(){
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
locationManager.distanceFilter = 1
locationManager.startUpdatingLocation()
//locationManager.startUpdatingHeading()
locationManager.allowsBackgroundLocationUpdates = true
//checkForLocationServices()
checkLocationAuthorizationStatus()
guard let myLatitude = locationManager.location?.coordinate.latitude else{
return
}
guard let myLongitude = locationManager.location?.coordinate.longitude
else{
return
}
showMap(myLatitude:myLatitude, myLongitude:myLongitude)
}
func showMap(myLatitude:Double, myLongitude:Double){
let camera = GMSCameraPosition.camera(withLatitude: myLatitude,
longitude: myLongitude, zoom: 17)
mapView = GMSMapView.map(withFrame: view.bounds, camera: camera)
//mapView = GMSMapView.map(withFrame: CGRect(x: 0, y: 0, width:
self.view.frame.width, height: self.view.frame.height - 250), camera: camera)
mapView?.center = self.view.center
self.view.addSubview(mapView!)
mapView.padding = UIEdgeInsetsMake(150, 0, 80, 0)
mapView.settings.myLocationButton = true
mapView.delegate = self
//mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
mapView.isMyLocationEnabled = true
frameForMapView.addSubview(mapView)
}
func checkLocationAuthorizationStatus() {
let status = CLLocationManager.authorizationStatus()
if status == CLAuthorizationStatus.notDetermined{
print("NotDetermined")
locationManager.requestWhenInUseAuthorization()
CLLocationManager.locationServicesEnabled()
locationManager.requestLocation()
}else {
print("Problem with authorization")
}
}
// Handle incoming location events.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location: CLLocation = locations.last!
print("Location: \(location)")
moveMyImageOnMap()
print(isStartRide, gotCounterFromLastRide , counter)
if isStartRide && gotCounterFromLastRide{
updateMyLocationToDataBase()
}
}
// func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) {
// print(newHeading)
// }
// Handle authorization for the location manager.
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
switch status {
case .restricted:
print("Location access was restricted.")
case .denied:
print("User denied access to location.")
// Display the map using the default location.
case .notDetermined:
print("Location status not determined.")
case .authorizedAlways: fallthrough
case .authorizedWhenInUse:
print("Location status is OK.")
if mapView != nil {
mapView.isMyLocationEnabled = true
}
}
}
// Handle location manager errors.
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
locationManager.stopUpdatingLocation()
print("Error: \(error)")
let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as! String
let msg :String = "You have denied the app to access your location. Please enable the location services in your settings for the app to get the location";
let alertController = UIAlertController(title: "Allow \(appName) to access your location while you are using the app?", message: msg, preferredStyle: .alert)
let cancelAction = UIAlertAction(title: "CANCEL", style: UIAlertActionStyle.default, handler: nil)
let settingsAction = UIAlertAction(title: "SETTINGS", style: .default) { (_) -> Void in
guard let settingsUrl = URL(string: UIApplicationOpenSettingsURLString) else {
return
}
if UIApplication.shared.canOpenURL(settingsUrl) {
UIApplication.shared.open(settingsUrl, completionHandler: { (success) in
print("Settings opened: \(success)") // Prints true
})
}
}
alertController.addAction(cancelAction)
alertController.addAction(settingsAction)
self.present(alertController, animated: true, completion: nil)
}
}

Check if location services are enabled

I've been doing some research about CoreLocation. Recently, I encountered a problem that has been covered elsewhere, but in Objective C, and for iOS 8.
I feel kinda silly asking this, but how can you check if location services are enabled using swift, on iOS 9?
On iOS 7 (and maybe 8?) you could use locationServicesEnabled(), but that doesn't appear to be working when compiling for iOS 9.
So how would I accomplish this?
Thanks!
Add the CLLocationManagerDelegate to your class inheritance and then you can make this check:
Import CoreLocation Framework
import CoreLocation
Swift 1.x - 2.x version:
if CLLocationManager.locationServicesEnabled() {
switch CLLocationManager.authorizationStatus() {
case .NotDetermined, .Restricted, .Denied:
print("No access")
case .AuthorizedAlways, .AuthorizedWhenInUse:
print("Access")
}
} else {
print("Location services are not enabled")
}
Swift 4.x version:
if CLLocationManager.locationServicesEnabled() {
switch CLLocationManager.authorizationStatus() {
case .notDetermined, .restricted, .denied:
print("No access")
case .authorizedAlways, .authorizedWhenInUse:
print("Access")
}
} else {
print("Location services are not enabled")
}
Swift 5.1 version
if CLLocationManager.locationServicesEnabled() {
switch CLLocationManager.authorizationStatus() {
case .notDetermined, .restricted, .denied:
print("No access")
case .authorizedAlways, .authorizedWhenInUse:
print("Access")
#unknown default:
break
}
} else {
print("Location services are not enabled")
}
iOS 14.x
In iOS 14 you will get the following error message:
authorizationStatus() was deprecated in iOS 14.0
To solve this, use the following:
private let locationManager = CLLocationManager()
if CLLocationManager.locationServicesEnabled() {
switch locationManager.authorizationStatus {
case .notDetermined, .restricted, .denied:
print("No access")
case .authorizedAlways, .authorizedWhenInUse:
print("Access")
#unknown default:
break
}
} else {
print("Location services are not enabled")
}
In objective-c
you should track user already denied or not determined then ask for permission or sent user to Setting app.
-(void)askEnableLocationService
{
BOOL showAlertSetting = false;
BOOL showInitLocation = false;
if ([CLLocationManager locationServicesEnabled]) {
switch ([CLLocationManager authorizationStatus]) {
case kCLAuthorizationStatusDenied:
showAlertSetting = true;
NSLog(#"HH: kCLAuthorizationStatusDenied");
break;
case kCLAuthorizationStatusRestricted:
showAlertSetting = true;
NSLog(#"HH: kCLAuthorizationStatusRestricted");
break;
case kCLAuthorizationStatusAuthorizedAlways:
showInitLocation = true;
NSLog(#"HH: kCLAuthorizationStatusAuthorizedAlways");
break;
case kCLAuthorizationStatusAuthorizedWhenInUse:
showInitLocation = true;
NSLog(#"HH: kCLAuthorizationStatusAuthorizedWhenInUse");
break;
case kCLAuthorizationStatusNotDetermined:
showInitLocation = true;
NSLog(#"HH: kCLAuthorizationStatusNotDetermined");
break;
default:
break;
}
} else {
showAlertSetting = true;
NSLog(#"HH: locationServicesDisabled");
}
if (showAlertSetting) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:nil message:#"Please enable location service for this app in ALLOW LOCATION ACCESS: Always, Go to Setting?" delegate:self cancelButtonTitle:#"No" otherButtonTitles:#"Open Setting", nil];
alertView.tag = 199;
[alertView show];
}
if (showInitLocation) {
[self initLocationManager];
}
}
Implement alertView Delegate then sent user to enable location service if already deny by user.
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (alertView.tag == 199) {
if (buttonIndex == 1) {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
}
return;
}
}
Init Location Manager
-(void)initLocationManager{
self.locationManager = [[CLLocationManager alloc] init];
if([self.locationManager respondsToSelector:#selector(requestAlwaysAuthorization)]) {
[self.locationManager requestAlwaysAuthorization];
}
}
Please note kCLAuthorizationStatusAuthorizedAlways and kCLAuthorizationStatusAuthorizedWhenInUse is difference.
Here is the format Apple recommends.
switch CLLocationManager.authorizationStatus() {
case .notDetermined:
// Request when-in-use authorization initially
break
case .restricted, .denied:
// Disable location features
break
case .authorizedWhenInUse, .authorizedAlways:
// Enable location features
break
}
Here is a complete example.
This includes an AlertView with a button to take the user to the Settings screen if previously denied access.
import CoreLocation
let locationManager = CLLocationManager()
class SettingsTableViewController:CLLocationManagerDelegate{
func checkUsersLocationServicesAuthorization(){
/// Check if user has authorized Total Plus to use Location Services
if CLLocationManager.locationServicesEnabled() {
switch CLLocationManager.authorizationStatus() {
case .notDetermined:
// Request when-in-use authorization initially
// This is the first and the ONLY time you will be able to ask the user for permission
self.locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
break
case .restricted, .denied:
// Disable location features
switchAutoTaxDetection.isOn = false
let alert = UIAlertController(title: "Allow Location Access", message: "MyApp needs access to your location. Turn on Location Services in your device settings.", preferredStyle: UIAlertController.Style.alert)
// Button to Open Settings
alert.addAction(UIAlertAction(title: "Settings", style: UIAlertAction.Style.default, handler: { action in
guard let settingsUrl = URL(string: UIApplication.openSettingsURLString) else {
return
}
if UIApplication.shared.canOpenURL(settingsUrl) {
UIApplication.shared.open(settingsUrl, completionHandler: { (success) in
print("Settings opened: \(success)")
})
}
}))
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertAction.Style.default, handler: nil))
self.present(alert, animated: true, completion: nil)
break
case .authorizedWhenInUse, .authorizedAlways:
// Enable features that require location services here.
print("Full Access")
break
}
}
}
}
SWIFT (As of July 24, 2018)
if CLLocationManager.locationServicesEnabled() {
}
this will tell you if the user has already selected a setting for the app's location permission request
It is just a 2 line function in Swift 4:
import CoreLocation
static func isLocationPermissionGranted() -> Bool
{
guard CLLocationManager.locationServicesEnabled() else { return false }
return [.authorizedAlways, .authorizedWhenInUse].contains(CLLocationManager.authorizationStatus())
}
For swift3.0 and above ,
if frequent checks are made for the availability of location services, create a class like below,
import CoreLocation
open class Reachability {
class func isLocationServiceEnabled() -> Bool {
if CLLocationManager.locationServicesEnabled() {
switch(CLLocationManager.authorizationStatus()) {
case .notDetermined, .restricted, .denied:
return false
case .authorizedAlways, .authorizedWhenInUse:
return true
default:
print("Something wrong with Location services")
return false
}
} else {
print("Location services are not enabled")
return false
}
}
}
and then use it like this in your VC
if Reachability.isLocationServiceEnabled() == true {
// Do what you want to do.
} else {
//You could show an alert like this.
let alertController = UIAlertController(title: "Location
Services Disabled", message: "Please enable location services
for this app.", preferredStyle: .alert)
let OKAction = UIAlertAction(title: "OK", style: .default,
handler: nil)
alertController.addAction(OKAction)
OperationQueue.main.addOperation {
self.present(alertController, animated: true,
completion:nil)
}
}
When you call -startLocation, if location services were denied by the user, the location manager delegate will receive a call to - locationManager:didFailWithError: with the kCLErrorDenied error code. This works both in all versions of iOS.
In Swift 3.0
if (CLLocationManager.locationServicesEnabled())
{
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
if ((UIDevice.current.systemVersion as NSString).floatValue >= 8)
{
locationManager.requestWhenInUseAuthorization()
}
locationManager.startUpdatingLocation()
}
else
{
#if debug
println("Location services are not enabled");
#endif
}
To ask for permission for location services you use:
yourSharedLocationManager.requestWhenInUseAuthorization()
If the status is currently undetermined an alert will show prompting the user to allow access. If access is denied your app will be notified in the CLLocationManagerDelegate, likewise if permission is denied at any point you will be updated here.
There are two separate statuses you need to check to determine the current permissions.
If the user has the general location services enabled or not
CLLocationManager.locationServicesEnabled()
If the user has granted the correct permission for your app..
CLLocationManager.authorizationStatus() == .authorizedWhenInUse
You could add an extension is a handy option:
extension CLLocationManager {
static func authorizedToRequestLocation() -> Bool {
return CLLocationManager.locationServicesEnabled() &&
(CLLocationManager.authorizationStatus() == .authorizedAlways || CLLocationManager.authorizationStatus() == .authorizedWhenInUse)
}
}
Here it is being accessed when the user has first requested directions:
private func requestUserLocation() {
//when status is not determined this method runs to request location access
locationManager.requestWhenInUseAuthorization()
if CLLocationManager.authorizedToRequestLocation() {
//have accuracy set to best for navigation - accuracy is not guaranteed it 'does it's best'
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
//find out current location, using this one time request location will start the location services and then stop once have the location within the desired accuracy -
locationManager.requestLocation()
} else {
//show alert for no location permission
showAlertNoLocation(locationError: .invalidPermissions)
}
}
Here is the delegate:
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if !CLLocationManager.authorizedToRequestLocation() {
showAlertNoLocation(locationError: .invalidPermissions)
}
}
Swift 5.2
First, set up User class as a CLLocationManager delegate:
import SwiftUI
import CoreLocation
class User: NSObject, ObservableObject {
let manager = CLLocationManager()
override init() {
super.init()
manager.delegate = self
manager.requestWhenInUseAuthorization()
manager.requestLocation()
manager.startUpdatingLocation()
}
}
extension User: CLLocationManagerDelegate {
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
print("Location services authorization request")
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("Location updated")
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Failed to find user's location: \(error.localizedDescription)")
}
}
Then in a view:
if (user.manager.authorizationStatus == .denied) {
print("Location authorization denied, displaying sheet.")
}
Here is my solution:
import CoreLocation
import Combine
class LocationManager: NSObject, CLLocationManagerDelegate {
static let shared = LocationManager()
private (set) var didChangeLocationAuthorization: CurrentValueSubject<CLAuthorizationStatus, Never> = .init(.notDetermined)
private let manager = CLLocationManager()
private let notificationCenter = NotificationCenter.default
var authorizationStatus: CLAuthorizationStatus = .notDetermined
private override init() { }
func checkLocationService() {
setupLocationManager()
checkLocationManagerAuthorization()
}
private func setupLocationManager() {
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
}
private func checkLocationManagerAuthorization() {
authorizationStatus = manager.authorizationStatus
switch authorizationStatus{
case .notDetermined:
print("::: -> Location: notDetermined")
manager.requestWhenInUseAuthorization()
case .authorizedAlways, .authorizedWhenInUse:
print("::: -> Location: authorizedWhenInUse")
manager.startUpdatingLocation()
case .denied, .restricted:
print("::: -> Location: denied")
default:
break
}
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
checkLocationManagerAuthorization()
didChangeLocationAuthorization.send(manager.authorizationStatus)
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
manager.stopUpdatingLocation()
}
}
Use like this:
LocationManager.shared.checkLocationService()
LocationManager.shared.didChangeLocationAuthorization
.sink { [weak self] authorization in
print("::: Location Permission: \(authorization)")
}.store(in: &cancelBag)

Refreshing location then running NSXMLParser

My app (1) gets the user's location then (2) parses XML based on that location data. From load, the app works great. But I would like to get updated XML based on a change of location when the user taps the refresh button. I've tried several versions of this but can't get to work. I've included the portion of my code I think is relevant to this question (I think it's a timing issue). On tapping the refresh button, the location updates but the old XML is loaded:
class Myclass: UIPageViewController, UIPageViewControllerDataSource, CLLocationManagerDelegate, NSXMLParserDelegate {
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
self.dataSource = self
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters
locationManager.requestWhenInUseAuthorization()
//locationManager.requestLocation()
}
override func viewWillAppear(animated: Bool) {
switch CLLocationManager.authorizationStatus() {
case .AuthorizedWhenInUse, .AuthorizedAlways:
busyAlertController.display()
locationManager.requestLocation()
print("Authorized")
case .NotDetermined:
locationManager.requestWhenInUseAuthorization() // or request always if you need it
print("Not Determined")
case .Restricted, .Denied:
print("Restricted or Denied")
self.dismissViewControllerAnimated(true, completion: nil)
let alertController = UIAlertController(
title: "Background Location Access Disabled",
message: "We need to know your location to show you the correct forecast, please open this app's settings and set location access to 'When in Use' or 'Always'.",
preferredStyle: .Alert)
let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel, handler: nil)
alertController.addAction(cancelAction)
let openAction = UIAlertAction(title: "Open Settings", style: .Default) { (action) in
if let url = NSURL(string:UIApplicationOpenSettingsURLString) {
UIApplication.sharedApplication().openURL(url)
}
}
alertController.addAction(openAction)
self.presentViewController(alertController, animated: true, completion: nil)
}
}
// MARK: UIPageViewControllerDataSource & UIPageViewControllerDelegate
// MARK: - CLLocationManagerDelegate
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
if (status == .AuthorizedAlways) || (status == .AuthorizedWhenInUse) {
locationManager.requestLocation()
}
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.first {
let lat = "\(location.coordinate.latitude)"
let lon = "\(location.coordinate.longitude)"
let url = baseURL + lat + "&lon=" + lon + suffixURL
guard let urlAsNSURL = NSURL(string: url) else {return}
NWSURL = urlAsNSURL
runParser()
} else {
//TODO:
}
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
print("Error finding location: \(error.localizedDescription)")
showAlert("Location Problem", message: "We're having trouble finding your location, please try again.")
}
//XMLParser Methods
func parserDidEndDocument(parser: NSXMLParser){
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.showVC()
})
}
func runParser() {
guard let url = URL else {
return}
guard let parser = NSXMLParser(contentsOfURL: url) else {return}
parser.delegate = self
parser.parse()
}
#IBAction func refresh(sender: UIBarButtonItem) {
locationManager.requestLocation()
//runParser()
}
}
The locations array which was passed into locationManager:didUpdateLocations: may contain more than one location in case updates were deferred or multiple locations arrived before they could be delivered.
Since it is organized in the order in which the updates occurred, the most recent location update is at the end of the array.
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.last {
...
}
}
The problem was that I wasn't clearing out a variable (array) after the NSXMLParser was done, and so I was appending over the stale data, yet since the stale data was first it was displaying in my UI and made it VERY hard to detect the problem until I printed to the console and saw the multiple arrays. I've done something similar before so note to anyone implementing NSXMLParser: make sure that you clear out the variable that you are using to store data in didEndElement.

NSLocation doesn't wait for me to click allow

When I run the code, the window pops asking for permission to use location but disappears almost immediately, not giving the user a chance to click "Allow". Is there a way to force this action before proceeding?
import UIKit
import MapKit
import CoreLocation
class MapViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate{
var map:MKMapView?
var manager:CLLocationManager!
convenience init(frame:CGRect){
self.init(nibName: nil, bundle: nil)
self.view.frame = frame
self.map = MKMapView(frame: frame)
self.map!.delegate = self
self.view.addSubview(self.map!)
}
override func viewDidLoad() {
super.viewDidLoad()
// Core Location
manager = CLLocationManager()
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
}
func locationManager(manager: CLLocationManager!,
didChangeAuthorizationStatus status: CLAuthorizationStatus){
print("The authorization status of location services is changed to: ")
switch CLLocationManager.authorizationStatus(){
case .Denied:
println("Denied")
case .NotDetermined:
println("Not determined")
case .Restricted:
println("Restricted")
default:
println("Authorized")
}
}
func displayAlertWithTitle(title: String, message: String){
let controller = UIAlertController(title: title,
message: message,
preferredStyle: .Alert)
controller.addAction(UIAlertAction(title: "OK",
style: .Default,
handler: nil))
presentViewController(controller, animated: true, completion: nil)
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
if CLLocationManager.locationServicesEnabled(){
switch CLLocationManager.authorizationStatus(){
case .Denied:
displayAlertWithTitle("Not Determined",
message: "Location services are not allowed for this app")
case .NotDetermined:
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
case .Restricted:
displayAlertWithTitle("Restricted",
message: "Location services are not allowed for this app")
default:
println("Default")
}
} else {
println("Location services are not enabled")
}
}
func locationManager(manager:CLLocationManager, didUpdateLocations locations:[AnyObject]) {
var userLocation:CLLocation = locations[0] as! CLLocation
var latitude:CLLocationDegrees = userLocation.coordinate.latitude
var longitude:CLLocationDegrees = userLocation.coordinate.longitude
var latDelta:CLLocationDegrees = 1.0
var lonDelta:CLLocationDegrees = 1.0
var span:MKCoordinateSpan = MKCoordinateSpanMake(latDelta, lonDelta)
var location:CLLocationCoordinate2D = CLLocationCoordinate2DMake(latitude, longitude)
var region:MKCoordinateRegion = MKCoordinateRegionMake(location, span)
map!.setRegion(region, animated: true)
manager.stopUpdatingLocation()
}
Please try this:
import UIKit
import GoogleMaps
import CoreLocation
class StartViewController: UIViewController,CLLocationManagerDelegate,GMSMapViewDelegate {
var locationManager: CLLocationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
setupLocationManager()
}
func setupLocationManager() {
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
locationHandler()
}
func locationHandler() {
if CLLocationManager.locationServicesEnabled() == true {
if (CLLocationManager.authorizationStatus() == .denied) {
// The user denied authorization
} else if (CLLocationManager.authorizationStatus() == .authorizedAlways) {
// The user accepted authorization
} else if (CLLocationManager.authorizationStatus() == .notDetermined){
// The user not determiend authorization
}else if (CLLocationManager.authorizationStatus() == .authorizedWhenInUse){
// In use
}else{ }
}else{
//Access to user location permission denied!
}
}
}//class
Be successful.
It's because you're calling manager.startUpdatingLocation() before you get the result from the manager.requestWhenInUseAuthorization(). Even though you call requestWhenInUseAuthorization, you're updating the user's location before you ever get the result of that method (I had the exact same question as you, actually!)
The answer to that question explains the solution well. Basically, you'll need to implement the locationManager:didChangeAuthorizationStatus delegate method, which is called any time the authorization status changes based on user input. If the user did authorize tracking, then you can call manager.startUpdatingLocation().
Also, for a Swift example of how to implement these methods, take look at this guide.

Resources