It is my general ViewController class , I research the this question iOS app doesn't ask for location permission , I already have NSLocationAlwaysUsageDescription and NSLocationWhenInUseUsageDescription
#IBOutlet weak var ownMapView: MKMapView!
let locationManager : CLLocationManager = CLLocationManager();
override func viewDidLoad() {
super.viewDidLoad();
self.locationManager.delegate = self;
self.locationManager.requestWhenInUseAuthorization();
self.locationManager.startUpdatingLocation();
}
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
guard status == .AuthorizedWhenInUse else
{
print("Location not using");
return;
}
print("Location using.");
ownMapView.showsUserLocation = true;
}
Even I added NSLocationAlwaysUsageDescription and NSLocationWhenInUseUsageDescription this is not working for me . It prints only "Location not using"
How do I request correctly ?
To understand this correctly check the CLLAuthorizationStatus doc:
https://developer.apple.com/library/ios/documentation/CoreLocation/Reference/CLLocationManager_Class/#//apple_ref/c/tdef/CLAuthorizationStatus
CLAuthorizationStatus has several possible values:
NotDetermined - User has not yet made a decision to allow/deny to use location
Restricted - App is restricted to use location
Denied - User has denied to use location.
AuthorizedAlways - This app is authorized to start location services at any time.
AuthorizedWhenInUse - App is authorized to start most location services while running in the foreground
You are checking only "AuthorizedWhenInUse" status but you launch first its status is NotDetermined.
You can further check the status like below:
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
if status == .NotDetermined
{
print ("Loction use not determined")
return
}
if status == .Denied
{
print ("Location determined")
return
}
guard status == .AuthorizedWhenInUse else
{
print("Location not using");
return;
}
print("Location using.");
ownMapView.showsUserLocation = true;
}
Have us added NSLocationAlwaysUsageDescription & NSLocationWhenInUseUsageDescription in plist? . Add these and it will work .
Related
How do I track in the background in iOS a user walking in Apple MapKit or Google Maps Platform for iOS and calculate the distance traveled so far using Swift and Xcode?
I have tried using MapKit. The problem I get is that when the iOS device is locked, Core Location stops tracking the device. When the device is unlocked and the app comes to the foreground, Core Location starts tracking again and draws a straight line from the last point tracked to the point tracked when the app comes to the foreground, thus skipping all the time that the device is locked. If Google Maps Platform works better for this purpose, I am willing to use it.
Following is the relevant code:
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.allowsBackgroundLocationUpdates = true
if #available(iOS 11.0, *) {
locationManager.showsBackgroundLocationIndicator = true
}
let status = CLLocationManager.authorizationStatus()
if status == .authorizedAlways || status == .authorizedWhenInUse || status == .restricted {
locationManager.startUpdatingLocation()
locationManager.startMonitoringSignificantLocationChanges()
} else {
locationManager.requestAlwaysAuthorization()
}
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedAlways || status == .authorizedWhenInUse || status == .restricted {
locationManager.startUpdatingLocation()
locationManager.startMonitoringSignificantLocationChanges()
} else {
locationManager.requestAlwaysAuthorization()
}
}
Did you add in your .plist,
Privacy - Location Always and When In Use Usage Description and
Privacy - Location When In Use Usage Description?
Then as you are probably conforming CLLocationManagerDelegate in the method didUpdateLocations you should be able to perform your code.
My mistake was that I was creating the line with the user location from the map instead of the user location passed to the didUpdateLocations method. The userLocation property on the map does not update in the background, whereas the userLocation property of CLLocationManager does update in the background.
Here is the corrected code, with a comment on the line that I corrected:
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("didUpdateLocations", locations)
if let userLocation = locations.first, polyline != nil {
let userLocationMapPoint = MKMapPoint(userLocation.coordinate)
let lastUserLocationMapPoint = MKMapPoint(route.last!)
totalDistance += userLocationMapPoint.distance(to: lastUserLocationMapPoint)
mapView.removeOverlay(polyline)
route.append(userLocation.coordinate) // changed from route.append(mapView.userLocation.coordinate)
polyline = MKPolyline(coordinates: route, count: route.count)
mapView.addOverlay(polyline)
}
print("latitude:", mapView.userLocation.coordinate.latitude, "longitude:", mapView.userLocation.coordinate.longitude)
}
I have an iOS application developed in Swift. My app is currently in Swift 2 but I am using Xcode 8 with Swift 3. The app is configured to use the legacy swift language version.
Until recently the app was working correctly.
The app asks for the correct rights for always use the location and the autorisation is correctly set to always.
I renewed the signing identity for the production app and the app stopped to be notified on a location update but was still working in development mode (launched from xcode).
Now I revoked and renew the production and development certificate and the app does not update the location while in background whereas the autorisation is set to always.
The app is correctly installed so I guess that the certificates are okay but I don't understand why the location is not updated in background.
I run the app on an iPhone 7 with IOS 10.2 and xcode automatically manage signing.
Here is my location manager configuration:
public class LocationManager : NSObject, ModuleManager, CLLocationManagerDelegate {
/// The core location manager
let coreLocationManager: CLLocationManager
public var datas: JSONable? {
get {
return LocationDatas(locations: self.locations)
}
set {
self.locations = newValue == nil ? [Location]() : newValue as? [Location]
}
}
/// The list of locations to send
private var locations: [Location]?
/// The last location
public var lastLocation: Location? {
return self.locations?.last
}
public override init() {
self.coreLocationManager = CLLocationManager()
if #available(iOS 9.0, *) {
self.coreLocationManager.allowsBackgroundLocationUpdates = true
}
// The accuracy of the location data.
self.coreLocationManager.desiredAccuracy = kCLLocationAccuracyBest;
// The minimum distance (measured in meters) a device must move horizontally before an update event is generated.
self.coreLocationManager.distanceFilter = 500; // meters
self.locations = [Location]()
super.init()
self.coreLocationManager.delegate = self
self.locationManager(self.coreLocationManager, didChangeAuthorizationStatus: CLLocationManager.authorizationStatus())
}
// MARK: - CLLocationManager Delegate
public func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
NSLog("location update")
guard locations.count > 0 else {
NSLog("Module Location -- no location available")
return
}
// Add all location waiting in the list to send
self.locations?.appendContentsOf(locations.map { Location(cllocation: $0) })
SDKManager.manager?.sendHeartbeat()
}
public func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
switch CLLocationManager.authorizationStatus() {
case .NotDetermined:
if #available(iOS 8.0, *) {
self.coreLocationManager.requestAlwaysAuthorization()
} else {
self.coreLocationManager.startUpdatingLocation()
}
case .Denied, .Restricted:
NSLog("Module Location -- access denied to use the location")
case .AuthorizedAlways:
NSLog("AuthorizedAlways")
self.coreLocationManager.startUpdatingLocation()
//self.coreLocationManager.startMonitoringSignificantLocationChanges()
default:
break
}
}
public func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
NSLog("Module Location -- error : \(error)")
}
}
The locationManager function is not called in background.
Here is my info.plist:
Here is the authorization on the phone:
The little location arrow is always there but no location update is logged.
I checked your code and it seems to be fine, revise if you have done these required settings
Enable location updates in Background mode
Add NSLocationAlwaysUsageDescription in your info.plist
If you did not do 1st point you app would have crashed but if did not do 2nd point your code will go through but you will never get updates.
Update:
It seems your LocationManager object is released in ARC. Can you try changing your LocationManager class to Singleton by added
static let sharedInstance = LocationManager()
And accessing LocationManager in your code like this
LocationManager.sharedInstance
You don't need to use App background Refresh just for Location update in Background. (It can be used for other maintenance work like DB cleaning, uploading, downloading, etc. while charging)
While initializing coreLocationManager, set the following properties as well
// It will allow app running location updates in background state
coreLocationManager.allowsBackgroundLocationUpdates = true
// It will not pause location automatically, you can set it true if you require it.
coreLocationManager.pausesLocationUpdatesAutomatically = false
I'm using the MapKit in my Swift iOS app.
The thing is that I'm requesting the permission to access the user location when the app is in use, but the first time I run the app in my iPhone, it stays frozen in the splash screen, because the permission request don't popup, but then, if I press the home button, the popup appears to ask for permission. And if I accept then, the next run the app works properly, but it shouldn't work like this.
So in the code, the debugger crashes here because he cannot get the permission:
let initialLocation:CLLocation = CLLocation(latitude: (locationManager.location?.coordinate.latitude)!, longitude: (locationManager.location?.coordinate.longitude)!)
Indicating the next issue: Thread 1: EXC_BREAKPOINT (code=1, subcode=0x1000b5d00)
So, I'm already asking the permissions in the viewWillAppear method:
let locationManager = CLLocationManager()
// Ask for Authorisation from the User.
// locationManager.requestAlwaysAuthorization()
// For use in foreground
locationManager.requestWhenInUseAuthorization()
//locationManager.requestAlwaysAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
//locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
mapView.showsUserLocation = true
}
And I also have the entry in the Info.plist: Privacy - Location When In Use Usage Description.
Why is the popup not showing in the foreground but in the background?
Thanks in advance for your help.
Cheers
EDIT:
I have an splash screen with the logo before the map view. Can this be the problem?
EDIT 2 in answer to #Dan Clark
Ok, I've added this check in the viewDidLoad as below:
EDIT 3
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
print("viewdidload")
if CLLocationManager.authorizationStatus() != .AuthorizedWhenInUse // Check authorization for location tracking
{
print("requestingautorization")
locationManager.requestWhenInUseAuthorization()
print("afterrequestingauthorization")
// LocationManager will callbackdidChange... once user responds
} else {
print("startupdatinglocation")
addPins(locationManager)
}
}
But the popup requesting the authorization is not appearing :( I've got both prints before and after but the popup is not showing.
I also added the function you wrote me, in the same class.
#nonobjc func locationManager(manager: CLLocationManager!, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
print("instatuscheck")
switch status
{
case .AuthorizedWhenInUse:
print("statusauthorized")
addPins(manager)
default:
print("statusdefault")
manager.requestWhenInUseAuthorization()
// User denied access, handle as appropriate
}
}
But I don't have it clear... this function will be called automatically when the authorization status changes?
Thanks again for your help :)
The problem is that it can take a while for you to get authorized by LocationManager after you make the request. Therefore, on your first try you don't have authorization before reaching the closure after your request. I've addressed this by testing for authorization and, if I don't have it, putting in the request and then waiting for the callback to didChangeAuthorizationStatus before starting location updates. If I already do have authorization, I immediately start location updates.
By the second time you run the app, you have the authorization so the delay doesn't occur and you're OK to go.
To try this approach, include this section in your ViewDidLoad (I'm assuming that you don't need to run this whenever your view appears, but only when it first starts):
if CLLocationManager.authorizationStatus() != .authorizedAlways // Check authorization for location tracking
{
locationManager.requestAlwaysAuthorization() // LocationManager will callbackdidChange... once user responds
} else {
locationManager.startUpdatingLocation()
}
And add this delegate function to your class to be called by LocationManager once you're authorized:
// If we've been authorized to use location, start the processes, otherwise abort the operation
// since we can't proceed without locations
#nonobjc func locationManager(manager: CLLocationManager!, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
switch status
{
case .authorizedAlways:
locationManager.startUpdatingLocation()
default:
// User denied access, handle as appropriate
}
}
Here's the code I use to instantiate / configure the locationManager:
lazy var locationManager: CLLocationManager = {
[unowned self] in
var _locationManager = CLLocationManager()
_locationManager.delegate = self
_locationManager.desiredAccuracy = [a user setting in my app]
_locationManager.allowsBackgroundLocationUpdates = true
_locationManager.pausesLocationUpdatesAutomatically = false // So doesn't shut off if user stops to rest
_locationManager.activityType = .fitness
_locationManager.distanceFilter = Double([a user setting in my app])
return _locationManager
}()
This has been working for me so hopefully it will help.
I'm trying to set up an on boarding where I'm asking the user for a few permissions including: Location, Notifications and Camera. I have 3 different View Controllers set up, each asking for one of the permissions and explaining why. On each of the view controllers I have a button at the bottom that says "Grant Permission".
When the user clicks the button I want the permission dialogue to pop up, and once the user clicks allow I want to transition to the next view controller.
Here is what I have right now:
class OnboardingStep2:UIViewController{
override func viewDidLoad() {
self.view.backgroundColor = StyleKit.orangeWhite()
}
#IBAction func getPermission(sender: AnyObject) {
dispatch_sync(dispatch_get_main_queue()) {
let locManager = CLLocationManager()
locManager.requestAlwaysAuthorization()
}
if (CLLocationManager.authorizationStatus() == CLAuthorizationStatus.Authorized) {
self.performSegueWithIdentifier("goToStep3", sender: self)
}
}
}
I've tried using dispatch to queue up the tasks, but when using async the permission dialogue pops up and then immediately it closes because the authorization check is run (I'm assuming). Using dispatch_sync, the dialogue is never shown.
What is the best way to do this, I want the permission dialogue to pop up first and once the user clicks allow i want to segue.
Conform to the CLLocationManagerDelegate
Then call this:
Swift 3.0
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
switch status {
case .notDetermined:
manager.requestLocation()
case .authorizedAlways, .authorizedWhenInUse:
// Do your thing here
default:
// Permission denied, do something else
}
}
Swift 2.2
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
switch status {
case .NotDetermined:
manager.requestLocation()
case .AuthorizedAlways, .AuthorizedWhenInUse:
// Do your thing here
default:
// Permission denied, do something else
}
}
Swift 5
Implement CLLocationManagerDelegate
and this function:
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
switch CLLocationManager.authorizationStatus() {
case .notDetermined:
// User has not yet made a choice
case .denied:
// User has explicitly denied authorization
case .restricted:
// This application is not authorized to use location services.
case .authorized, .authorizedAlways, .authorizedWhenInUse:
// User has granted authorization
default:
// Other
}
}
If user has explicitly denied authorization for this application, or location services are disabled in Settings, it will return Denied status. How can I know the exact reason of it?
I've made this two function to check for each case
If user explicitly denied authorization for your app only you can check it like this,
+ (BOOL) isLocationDisableForMyAppOnly
{
if([CLLocationManager locationServicesEnabled] &&
[CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied)
{
return YES;
}
return NO;
}
If location services are disabled in Settings,
+ (BOOL) userLocationAvailable {
return [CLLocationManager locationServicesEnabled];
}
And I'm using it like this,
if([UserLocation userLocationAvailable]) {
//.... Get location.
}
else
{
if([UserLocation isLocationDisableForMyAppOnly]) {
//... Location not available. Denied accessing location.
}
else{
//... Location not available. Enable location services from settings.
}
}
P.S. UserLocation is a custom class to get user location.
Swift
Use:
CLLocationManager.authorizationStatus()
To get the authorization status. It's a CLAuthorizationStatus, you can switch on the different status' like this:
let status = CLLocationManager.authorizationStatus()
switch status {
case .authorizedAlways:
<#code#>
case .authorizedWhenInUse:
<#code#>
case .denied:
<#code#>
case .notDetermined:
<#code#>
case .restricted:
<#code#>
}
Swift:
I've made this two function to check for each case
If user explicitly denied authorization for your app only you can check it like this,
class func isLocationDisableForMyAppOnly() -> Bool {
if CLLocationManager.locationServicesEnabled() && CLLocationManager.authorizationStatus() == .denied {
return true
}
return false
}
If location services are disabled in Settings,
class func userLocationAvailable() -> Bool {
return CLLocationManager.locationServicesEnabled()
}
And I'm using it like this,
if UserLocation.userLocationAvailable() {
//.... Get location.
} else {
if UserLocation.isLocationDisableForMyAppOnly() {
//... Location not available. Denied accessing location.
} else {
//... Location not available. Enable location services from settings.
}
}
P.S. UserLocation is a custom class to get user location.
Swift 5.0 iOS 14.0
Since 'authorizationStatus()' was deprecated in iOS 14.0 you should use the instance directly.
authorizationStatus is now a property of CLLocationManager.
You can use the following example:
import CoreLocation
class LocationManager {
private let locationManager = CLLocationManager()
var locationStatus: CLAuthorizationStatus {
locationManager.authorizationStatus
}
}
Have also a look at this question: AuthorizationStatus for CLLocationManager is deprecated on iOS 14