how to request auth for CLLocationManager on macOS - swift

I'm writing an OSX app in swift 3 that uses CLLocationManager, according to the docs and all the examples I've found the following should be fine (this is in a class that that is a CLLocationManagerDelegate)
if CLLocationManager.locationServicesEnabled() {
let lm = CLLocationManager()
lm.requestWhenInUseAuthorization()
lm.delegate = self
lm.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
lm.startUpdatingLocation()
print("should start getting location data")
} else {
print("Location service disabled");
}
but it seems requestWhenInUseAuthorization (and requestAlwaysAuthorization) aren't available to OSX. I've currently got those function calls wrapped in #if blocks:
#if os(macOS)
// can't find way for MacOSX to request auth
#endif
#if os(watchOS) || os(tvOS)
lm.requestWhenInUseAuthorization()
#endif
#if os(iOS)
lm.requestAlwaysAuthorization()
#endif
So does anyone know how to get this working in a macOS desktop application?

According to the Core Location Best Practices WWDC 2016 session,
For macOS, we only support always authorization. Furthermore, Core
Location will automatically display a prompt when you attempt to
access location information.
You don't need to call requestAlwaysAuthorization on macOS.
Don't forget to turn on "Location" under the "App Sandbox" capability for your target. Also, in my tests, the prompt was only shown the first time the app was run.

steps to trigger the auth request in OSX
1) set up location manager with delegate
manager = CLLocationManager()
manager.delegate = self
2) implement required delegate methods (these are marked optional, but they're not!)
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
//Do Stuff
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
//Error
}
3) request the location
manager.requestLocation()
That's all. The 'XXX Would like to use your current location' popup will now appear

Related

Can I adjust the frequency of locationManager (_: didUpdateLocations :)?

In the simulator, this method is executed only once.
However, when tested with a real mobile phone, it runs as many times as it is at the same location.
I want to control the frequency of this method.
Or is it possible through the distanceFilter?
What should I do if possible?
I suspect that the locationManager (_: didUpdateLocations :) method is automatically run whenever the location changes, so I can not control it.
Is there a way?
import UIKit
import CoreLocation
class MainViewController: UIViewController, CLLocationManagerDelegate {
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// my source code
}
}
Don't use the function func locationManager(_ manager: CLLocationManager, didUpdateTo newLocation: CLLocation, from oldLocation: CLLocation) it has been deprecated since iOS 6.
deprecated method
Setting the desired accuracy should fix the problem of getting many updates with relatively the same point. For tracking with a little less precision maybe try
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
If you are concerned with long running tracking tasks/jobs then consider implementing
func locationManagerDidPauseLocationUpdates(CLLocationManager)
func locationManagerDidResumeLocationUpdates(CLLocationManager)
They will inform you the the location is not changing and to save power the device will shut down some hardware.
CLLocationMangager
CLLocationManagerDelegate
You can set location accuracy like
self. locationManager.desiredAccuracy = kCLLocationAccuracyBest
See https://developer.apple.com/documentation/corelocation/cllocationaccuracy
I am not sure that you can control It. You can use func locationManager(_ manager: CLLocationManager, didUpdateTo newLocation: CLLocation, from oldLocation: CLLocation) { } method. This method gives you last location and new location and on the behalf of these locations, you can perform your desired function.
Or you can refer this answer click here
Locations don’t change with the simulator. You’ll only get a single call from the didupdatelocations callback. Running on a real device will cause the completion handler to continuously fire. You can simulate a location with a GPX file but I don’t believe it fires more that once using the simulator.
Desired accuracy does change the frequency of firing but the frequency also depends on the GPS hardware and battery life status. iOS control the frequency to a certain point to optimize device performance and longevity

Custom locations on Xcode 9+ only causes didUpdateLocations to only fire once

I have an app where I leverage CoreLocation. For some reason when I use Xcode 9 or even the 9.1 beta 2, setting custom location coordinates under Debug > Locations > Custom Location in the iPhone simulator doesn't work as expected.
When I apply the custom coordinates, didUpdateLocations is only called once, or three times, then the location services turn off, and the location arrow turns into an outline. I know most of you guys will say that I shouldn't be testing location services on the simulator, but I do not have a choice, as I do not have access to a device at the moment.
I have provided some simple code bellow for accessing the users location and update it continuously. If you could, please test this out on your iPhone simulator in Xcode 9+ (ios 11+) and set a custom latitude and longitude to Debug > Locations > Custom Location and see if you get the same issue.
import UIKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.requestWhenInUseAuthorization()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.first {
print(location.coordinate)
}
}
}

CLLocationManager is slow getting location, Swift

Im creating this app, and it needs to get the users location - its all working properly, the thing is, that the time from accepting the use of location services, to getting the actual location takes like 5 seconds - is this normal?
I've used other apps, where it goes much faster..
Here's what my code looks like:
override func viewDidLoad() {
super.viewDidLoad()
// Ask for Location-Authorisation from the User.
self.locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
locationManager.requestLocation()
}
mapView.delegate = self
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let locValue: CLLocationCoordinate2D = manager.location!.coordinate
let initialLocation = CLLocation(latitude: locValue.latitude, longitude: locValue.longitude)
self.centerMapOnLocation(initialLocation)
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
print("could not get location")
}
But the time from the application gets the location to put into the centerMapOnLocation-function, seems just to be quite long. What is to be expected, when getting a users location? I'm testing on a wifi connection, so I know its not because the internet is slow, or its a bad connection...
Anyone have an idea? :)
Best regards!
Try setting the accuracy and use locationManager.startUpdatingLocation(). I do that, and get answer within a second (on the device).
From the documentation of requestLocation():
This method returns immediately. Calling it causes the location manager to obtain a location fix (which may take several seconds) and call the delegate’s locationManager(_:didUpdateLocations:) method with the result.
Source
So basically, everything is fine with your code, it's just how the framework is built.
When initializing your location manager, add startUpdatingLocation():
let manager = CLLocationManager()
manager.delegate = self
manager.requestWhenInUseAuthorization()
manager.requestLocation()
manager.startUpdatingLocation()
Without startUpdatingLocation() geo-location takes about 5 seconds, with it, the request executes nearly immediately.
If you don't want to delay the app's launch for the location manager, consider deploying two location managers (in the app delegate), tasking one with generating a location quickly and the other with generating a location accurately:
fastLoc.delegate = self
fastLoc.desiredAccuracy = kCLLocationAccuracyThreeKilometers
fastLoc.requestWhenInUseAuthorization()
fastLoc.startUpdatingLocation()
bestLoc.delegate = self
bestLoc.desiredAccuracy = kCLLocationAccuracyBest
bestLoc.requestWhenInUseAuthorization()
bestLoc.requestLocation()
The combination of 3 km accuracy with startUpdatingLocation() should return a location almost instantly, almost always before the root view controller is even ready to go. bestLoc manager is likely to return a location well after the user has launched the app but it will be very accurate.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
switch manager {
case fastLoc:
fastLoc.stopUpdatingLocation()
deviceLocation = locations.last! // set property with fast loc
case bestLoc:
deviceLocation = locations.last! // overwrite property with best loc
default:
break
}
}

Trouble getting one-time user location in Swift

I am trying to get the user's location at the time of a button press, however I am having trouble setting up the location code. I've been looking between various answers on Stackoverflow but I can't get anything to work for me.
class MyViewController: UIViewController, CLLocationManagerDelegate {
var locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
if status == .AuthorizedAlways || status == .AuthorizedWhenInUse {
let latitude = self.locationManager.location?.coordinate.latitude
let longitude = self.locationManager.location?.coordinate.longitude
print("coordinates:")
print(latitude)
print(longitude)
}
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let locationValue : CLLocationCoordinate2D = manager.location!.coordinate
print("locations = \(locationValue.latitude) \(locationValue.longitude)")
}
However I just get nils from the prints in didChangeAuthorizationStatus and nothing ever prints from didUpdateLocations. Im also really confused about where to put startUpdatingLocation(). Some answers place it directly in viewDidLoad as I have here, others place it in didChangeAuthorizationStatus, and I saw another place it inside an if CLLocationManager.locationServicesEnabled() in didLoad. What is the correct way to do this? And also what have I done wrong that I'm not getting any values printing? I'm running on the emulator, have location turned on and allowed permissions in the pop-up, and have the emulator set to a lat/long custom location, and I have added the values in pList.
edit: even tried copy/pasting the code from this to get user's location only once but it returns (kCLErrorDomain error 0.). I added a 'requestWhenInUseAuthorization()` to their code but it still fails.
get nils from the prints in didChangeAuthorizationStatus
Because we don't have any locations yet. That's the wrong place to ask for that information. All that happened is that we are now authorized.
nothing ever prints from didUpdateLocations
Because you're running on the simulator, perhaps. Try running on a device.
And, if your goal is just to get the location and stop, don't use startUpdatingLocation; call requestLocation. That's what it's for.

Not ask permission to access to coreLocation and crash in swift

I have CoreLocation in my Swift app. When I run this in simulator or in a device, this crash and not show the permissions to access CoreLocation.. I have all code necessary to implement this: request in code, NSLocationWhenInUseUsageDescription or NSLocationAlwaysUsageDescription in plist...
I'm reading iOS app doesn't ask for location permission but I can't make that this show.
var location: CLLocationManager!
let status = CLLocationManager.authorizationStatus()
location=CLLocationManager()
if(status == CLAuthorizationStatus.NotDetermined) {
self.location.requestAlwaysAuthorization();
}
else {
location.startUpdatingLocation()
}
location.delegate = self
location.desiredAccuracy=kCLLocationAccuracyBest
self.location.startMonitoringSignificantLocationChanges()
print(location.location.coordinate.latitude) //Here crash
print(location.location.coordinate.longitude)
What other things can I make for show this?
Thanks!
Here is a code snippet from my app:
let locationManager = CLLocationManager()
...
locationManager.delegate = self
locationManager.activityType = CLActivityType.Fitness
locationManager.distanceFilter = 10 // 10m
if ( UIDevice.currentDevice().systemVersion == "8.0" ) {
locationManager.requestAlwaysAuthorization()
}
// get current location
if CLLocationManager.authorizationStatus() == CLAuthorizationStatus.Authorized {
locationManager.startUpdatingLocation()
}
The prompt only shows if the status is .NotDetermined. There are other statuses though that would prevent the location services from working (all but .Authorized) when the prompt does not get displayed. You should check for these.
Also, the requestAlwaysAuthorization does not return immediately and you need to implement
func locationManager(manager: CLLocationManager!, didChangeAuthorizationStatus status: CLAuthorizationStatus)
You have to add a row to info.plist file
NSLocationWhenInUseUsageDescription which contains a string.
That string will be displayed when the alert pops up asking user's permission.