I am doing an app wich uses the current user location and of it's friend's agenda.
I wrote a little bit of code which is suppossed to show your location
var loc :CLLocationCoordinate2D = self.mapView.userLocation.coordinate
var region = MKCoordinateRegionMakeWithDistance(loc, 5, 5)
self.mapView.setRegion(region, animated: true)
Furthermore I am getting the error saying :
Trying to start MapKit location updates without prompting for location authorization. Must call -[CLLocationManager requestWhenInUseAuthorization] or -[CLLocationManager requestAlwaysAuthorization] first.
After some research I don't understand what I'm supposed to do to improve my location[i changed from debug->simulateLocation but nothing changed]
Can you please explain what I have to keep in mind and what I need to do to improve my app?
[edit1] Is it possible that the simulator behaves this way and the phone should not behave the same way?
As of iOS 8 you have to explicitly ask the user for permission:
var systemVersion = NSOperatingSystemVersion(majorVersion: 8, minorVersion: 0, patchVersion: 0)
if NSProcessInfo.processInfo().isOperatingSystemAtLeastVersion(systemVersion) {
locationManager.requestAlwaysAuthorization()
}
// get current location
if CLLocationManager.authorizationStatus() == CLAuthorizationStatus.Authorized {
locationManager.startUpdatingLocation()
}
Furthermore you have to explicitly declare the permission question in your Info.plist
NSLocationAlwaysUsageDescription
I figured this issue out by google-ing it a little bit:
http://rshankar.com/get-your-current-address-in-swift/
One of the steps is going to settings->General->...Use Location->Always,This is suppossed to solve it and you will not get that error.
Furthermore the person that made the tutorial mentioned that you should enter a location just ot be sure it works and VOILA!
Related
Since recently, I get in the Xcode logs a lot of strange warnings from the CoreMotion framework, related to function getLocationForBundleID:
[Client] {"msg":"#NullIsland Either the latitude or longitude was exactly 0! That's highly unlikely", "latIsZero":0, "lonIsZero":0}
[Client] {"msg":"#NullIsland Received a latitude or longitude from getLocationForBundleID that was exactly zero", "latIsZero":0, "lonIsZero":0, "location":'28 5B E0 D7 EB 7F 00 00'}
I do not see any malfunction of my app. So maybe these logs can be ignored, but they are annoying anyway.
My questions are:
How can I correct a potential error?
How can I stop these warnings?
Apparently this error message is logged if property location of a CLLocationManager is read, before the CLLocationManager delivered the very first location.
My solution is the following:
I use anyway a subclass of CLLocationManager that allows me to set the location property for tests programmatically.
This is done as follows:
private var _location: CLLocation?
#objc dynamic override var location: CLLocation? {
get {
let usedLocation = _location ?? super.location // Here the error is logged if no location has been delivered yet
return usedLocation
}
set {
_location = newValue
}
}
This subclass has now an additional property
var anyLocationDelivered = false
that is set true in
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
locationManager.anyLocationDelivered = true
// …
}
and the getter is now
get {
guard anyLocationDelivered else { return nil }
let usedLocation = _location ?? super.location
return usedLocation
}
Now this error message is no longer logged.
I was getting this same error when trying to get a user's location in an app. There were two things preventing me from getting a valid lat/long. The main issue I was having was that I was using a simulator and had to set the location manually. If you're using the simulator, go to XCode, click Debug --> Simulate Location, and choose a location. I also had to make sure the correct location permission keys were added to Info.plist. You're also probably already aware, but you'll know if any keys are missing from Info.plist because a separate error message will print out to the console above or below these lat/long error messages, and it will tell you which key is missing. So basically:
Make sure you have all necessary location permissions accepted on the device/simulator being used to test the application.
Once you have location permissions, set a location if using a simulator.
Not sure if this will help at all, but I hope it does. If you've already confirmed that these are not issues, I'm not sure what may be causing the issue in your case, but stepping through the parts of your code responsible for fetching the user location would probably be beneficial to see which link in the process is failing.
The issue is likely that you ran your application on the simulator, which does NOT use your current location by default unless you manually change the simulation location in Debug, Simulate Location.
To fix the issue, either simulate your current location (as stated above) OR run your application on your physical signed device, close the application completely, and then relaunch your application.
OS_ACTIVITY_MODE = disable
To get rid of the warning do the following:
Steps
Navigate to Product -> Scheme -> Edit Scheme.
Open the Arguments tab
Environment Variables section add OS_ACTIVITY_MODE = disable
I'm stumped, iOS 11.4 ( 15F79 ), iPhone 6. Cannot get the App to Ask for Motion Data. info.plist has been set via the editor and double checked via the info.plist open in textWrangler, Also deleted key and saved via textWrangler.
<key>NSMotionUsageDescription</key>
<string>This app needs your Phones motion manager to update when the phone is tilted. Please allow this App to use your phones tilt devices</string>
I have deleted then reinstalled the app about 10 times. I have restared the phone 5 times. I have checked through settings and my app does NOT show up in Privacy-Motion and Fitness or anywhere else in settings. I am using a free developer account, maybe that has something to do with it?
I created a new Xcode game template and changed nothing apart from importing CoreMotion and this code
**** Edited, sorry I forgot to say I had started the instance, just forgot to put it here, just in case someone thinks that's the problem ************
let motionManager = CMMotionManager()
override func didMove(to view: SKView) {
motionManager.startDeviceMotionUpdates()
if motionManager.isDeviceMotionActive == true {
motionManager.accelerometerUpdateInterval = 0.2
motionManager.startAccelerometerUpdates(to: OperationQueue.current!, withHandler: {
(accelerometerData: CMAccelerometerData!, error: NSError!) in
let acceleration = accelerometerData.acceleration
print(accelerometerData)
} as! CMAccelerometerHandler)
}else{
print(CMMotionActivityManager.authorizationStatus().rawValue)
}
which prints a 0 ( an Enum - case not determined ) to the console.
In my actual app it was a 3 ( same Enum - case Denied ).
As I've said, I have uninstalled, reinstalled, edited plist via Xcode and text wrangler ( a code editor ) , tried different versions of the code above, tried the code in different places ( in did move to view, in class )tried code off apple docs. etc.... I haven't been asked the NSUsage question and the App keeps crashing.
I have looked for ways to get the Alert fired up, As in CLLocationManager.requestWhenInUseAuthorization() but I cannot find a comparable CMMotion version ( I don't think there is one. ) I have created a new swift file , imported Foundation and CMMotion and just put that code there, But still no Alert asking for Motion Data.
I tried a single view app template instead of a game template thinking that might be the issue, Nope.
What do I do?
Any help Appreciated. Thanks
You are confusing two related but different classes.
CMMotionManager gives access to accelerometer, magnetometer and gyroscope data. It does not require any user permission as this information is not considered privacy related.
In your else clause you are checking the authorisation status of CMMotionActivityManager. This object reports the device motion type (walking, running, driving). This information is considered privacy related and when you create an instance of this class and request data from it, the permissions alert is displayed.
The reason your else is being triggered is because you are checking isDeviceMotionActive; this will be false until you call startDeviceMotionUpdates, which you never do. Even if you used isAccelerometerActive you would have a problem because you call startAccelerometerUpdates in the if clause which will never be reached.
You probably meant to check isAccelerometerAvailable. If this returns false then there isn't much you can do; the device doesn't have an accelerometer.
Update
It doesn't make sense to check isDeviceMotionActive immediately after calling startDeviceMotion:
You know it's active; you just started it
I imagine the start up takes some time, so you could expect to get false if you check immediately.
Apple recommends that you do not have more than one observer in place for each motion device type, so the purpose of check the is...Active to ensure you don't call start... again if you have already done so.
If you only want gyroscope data then you don't need to call startDeviceMotionUpdates at all.
I'm a relatively new swift developer and I've heard in iOS 8 you can send local notifications based on a users Location. I have had a look at some code, particularly this one to create simple time based local notifications.
var leftNotification1:UILocalNotification = UILocalNotification()
leftNotification1.alertBody = "NotidicationA"
leftNotification1.repeatInterval = NSCalendarUnit.CalendarUnitHour
leftNotification1.fireDate = NSDate(timeIntervalSinceNow: 900)
UIApplication.sharedApplication().scheduleLocalNotification(leftNotification1)
I have also seen that you can replace fireDate with a location trigger like something like this:
var localNotification:UILocalNotification = UILocalNotification()
localNotification.regionTriggersOnce = true
localNotification.region = CLCircularRegion(circularRegionWithCenter: CLLocationCoordinate2D(latitude: 37.33233141, longitude: -122.03121860), radius: 50.0, identifier: "Location1")
UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
I know with locations you need permission from the user and poll for locations and that. I am not aware of how to do that sort of thing and link it with that code. When I just enter this code into my ViewDidLoad like I do for the top one it doesn't work for obvious reasons, the CoreLocation isn't registered and its not polling for locations. If someone could inform me about how to get this code working or even better give me an example code to take a look at that'll be great. Thanks.
I found a few guides that had different ways of approaching this, you need to import CoreLocation then you'll want to register the CLLocationManagerDelegate in the ViewController function in ViewController.swift.
You'll need to get permission for using the location with
locationManager.delegate = self
locationManager.requestAlwaysAuthorization()
UIApplication.sharedApplication().cancelAllLocalNotifications()
If you use the requestWhenInUseAuthorization instead it won't display the notifications as they only are displayed when you are not in the app. In iOS 8 it requires you to declare NSLocationAlwaysUsageDescription in the info.plist file you'll just enter some text to appear when location access is requested.
Once you've done all of that you can start creating your notifications, you can use the following code to do that
let locattionnotification = UILocalNotification()
locattionnotification.alertBody = "You have entered a high risk zone (Crown Range Road) , proceed with caution"
locattionnotification.regionTriggersOnce = false
locattionnotification.region = CLCircularRegion(circularRegionWithCenter: CLLocationCoordinate2D(latitude:
37.33182, longitude: -122.03118), radius: 100.0, identifier: "Location1")
UIApplication.sharedApplication().scheduleLocalNotification(locattionnotification)
The regionTriggersOnce option allows you to turn on and off if you get multiple messages when you enter and exit the zone or only one when you first enter the area.
You can change the latitude, longitude and radius (in meters) values to what you want and when you get within the radius of the point you will be alerted with the contents of the message.
For more details about the different options have a look at https://developer.apple.com/library/ios/documentation/iPhone/Reference/UILocalNotification_Class/
The SetSDK allows you to set this up really really simply now. Basically, just a few lines of code.
SetSDK.instance.onDeparture(from: .any) { departed in
/* do your Swift things here */
let departureCoordinates = departed.location
// keep going
}
SetSDK.instance.onArrival(to: .any) { arrived in
/* do your Swift things here */
let arrivalCoordinates = arrived.location
// keep going
}
The SDK handles doing the persistent location collection without killing your battery and the locations that it sends notifications for are continuously being learned, so no manual entry of geofences or anything like that. Just start it and go.
I'm trying to use some fancy iBeacons without success, kCLAuthorizationStatusNotDetermined all time.
According to other questions it's a requirement to add those keys to info.plist (some questions says one, other says both). According to an article for iBeacons I need the Always option.
<key>NSLocationWhenInUseUsageDescription</key>
<string>Nothing to say</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Permiso para acceder siempre</string>
At viewDidAppear:
self.locManager = [[CLLocationManager alloc]init];
self.locManager.delegate = self;
[self.locManager requestAlwaysAuthorization];
NSUUID* region1UUID = [[NSUUID alloc]initWithUUIDString:#""]; //ibeacon real UUID between "". Checked it's not nil.
self.beaconRegion = [[CLBeaconRegion alloc]
initWithProximityUUID:proximityUUID
identifier:#"myCoolString"];
self.beaconRegion.notifyEntryStateOnDisplay = YES;
self.beaconRegion.notifyOnEntry = YES;
self.beaconRegion.notifyOnExit = NO;
[self.locManager startMonitoringForRegion:self.beaconRegion];
[self.locManager startRangingBeaconsInRegion:self.beaconRegion];
Icon didn't appear at Settings/Privacy/Location until it was executed one of the two last methods. The Alert View to approve permissions never appears.
If I perform a manual change at Location Settings and check it it will change status but at a few moments later Location at Settings will delete "Always" status for my app and will leave it blank again. Later I check with no luck
-(void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
Any ideas what is missing or wrong? Thank you
For iOS 11 developers, you should take a look at this post: Location Services not working in iOS 11.
TL;DR: you need ALL THREE location keys in the Info.plist:
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>...</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>...</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>...</string>
Along with InfoPlist.strings translations in case of multilingual apps.
Had exactly the same problem.
It turned out that in my case the NSLocationAlwaysUsageDescription was required in my InfoPlist.strings localization files as well. Having NSLocationAlwaysUsageDescription in Info.plist wasn't enough...
I have noticed that if your instance of CLLocationManager is destroyed before the alert shows, you will never see the alert.
In my case I was creating a local variable of the location manager in the AppDelegate to ask for permission.
CLLocationManager *locationManager = [[CLLocationManager alloc] init];
if ([locationManager respondsToSelector:#selector(requestAlwaysAuthorization)]) {
[locationManager requestAlwaysAuthorization];
}
Changing the local variable to an instance variable made the alert to display:
#interface AppDelegate () {
CLLocationManager *_locationManager;
}
#end
_locationManager = [[CLLocationManager alloc] init];
if ([_locationManager respondsToSelector:#selector(requestAlwaysAuthorization)]) {
[_locationManager requestAlwaysAuthorization];
}
I got a similar problem recently with IOS 11 when I tried to use
locationManager.requestAlwaysAuthorization()
The solution for me was to add all the 4 permissions in Info.plist to get the alert:
Privacy - Location When In Use Usage Description
Privacy - Location Usage Description
Privacy - Location Always Usage Description
Privacy - Location Always and When In Use Usage Description
Just add this lines to your .plist file
<key>NSLocationAlwaysUsageDescription</key>
<string>Optional message</string>
Try to Start Updating Location (have helped for me)
[self.locationManager startUpdatingLocation];
For ios 11 Its important to add below mention new permission which is
NSLocationAlwaysAndWhenInUseUsageDescription
along with the old permission which is
NSLocationAlwaysUsageDescription, NSLocationWhenInUseUsageDescription
to fix the requestAlwaysAuthorization not showing permission alert.
Hardware-dependent System Issue?
iOS 13.1.3, iPhone 7 (model ID "iPhone9,1")
Seems like system Settings->Privacy->Location in some way cache permissions even after app deletion (at least, during the same iOS session and for specified configuration).
If 'allow once' is pressed and user deletes and reinstalls and runs the app again, didChangeAuthorizationStatus(_:) sends initial status .denied. (And therefore, according to CLLocationManager logic it does not show prompt alert after requestAlwaysAuthorization()).
This .denied status was the root of the evil in this case.
If user folds the app between initial didChangeAuthorizationStatus(:) and requestAlwaysAuthorization(), this causes next update of didChangeAuthorizationStatus(:) with status other than .denied and prompt alert will be shown.
iOS 13.1.3, iPhone 7 (model ID "iPhone9,3") - issue does not reproduce. (initial status received is .notDetermined)
iOS 13.1.2 iPhone 8 - all ok, same as above
Found, documented at forums and tested it's an Objective-C iOS 8 related bug.
Same code written in Swift just works. Working swift code, delegate is call.
Add Core Location framework to
Project Settings / Targets / Capabilities / Background Modes set "Location Updates" and "Uses Bluetooth LE Accessories"
Add key at Info.plist NSLocationAlwaysUsageDescription
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
var locManager: CLLocationManager?
override func viewDidLoad() {
super.viewDidLoad()
self.locManager = CLLocationManager();
self.locManager!.delegate = self;
if (!CLLocationManager.locationServicesEnabled()) {
println("Location services are not enabled");
}
self.locManager!.requestAlwaysAuthorization();
self.locManager!.pausesLocationUpdatesAutomatically = false;
let uuidString = "" // My ibeacon string there
let beaconIdentifier = "myCompany"
let beaconUUID:NSUUID = NSUUID(UUIDString: uuidString)
let beaconRegion:CLBeaconRegion = CLBeaconRegion(proximityUUID: beaconUUID,
identifier: beaconIdentifier)
self.locManager!.startMonitoringForRegion(beaconRegion)
self.locManager!.startRangingBeaconsInRegion(beaconRegion)
self.locManager!.startUpdatingLocation()
}
Dialog appears OK
Make sure you add the keys to the correct Info.plist file. Don't forget there's one for your App and one for AppTest.
Swift 3.X Latest code easily usage
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)")
}
I copied this tutorial ...
http://willd.me/posts/getting-started-with-ibeacon-a-swift-tutorial
It didn't work out the box, although the fix was very simple, don't declare
let locationManager = CLLocationManager()
But move it into the class as variable
var locationManager = CLLocationManager()
And it works!!
It will show a prompt when the location permission for your app isn't set and once when 'in use' location permission for your app is set, subsequent calls do not show (and I don't think there is any API feedback that the call was swallowed).
From Apple docs:
Discussion When the current authorization status is notDetermined ,
this method runs asynchronously and prompts the user to grant
permission to the app to use location services. The user prompt
contains the text from the NSLocationAlwaysUsageDescription key in
your app’s Info.plist file, and the presence of that key is required
when calling this method. After the status is determined, the location
manager delivers the results to the delegate’s
locationManager(:didChangeAuthorization:) method. If the current
authorization status is anything other than notDetermined, this method
does nothing and does not call the
locationManager(:didChangeAuthorization:) method, with one exception.
If your app has never requested always authorization and its current
authorization status is authorizedWhenInUse , you may call this
method one time to try and change your app's authorization status to
authorizedAlways .
Please check this Reference link
It has described all the changes regarding iOS 11. Your issue is also seems to be one among the cases it described. It might give you an idea what changes you need to make in your code to correct the issue.
For me on iOS 13, I had to reset the Simulator to trigger the permission prompt again.
To add to that: be sure you have added the array UIBackgroundModes to your .plist and inserted the "location" value.
Apple will not tell you it is missing and give you WhenInUse instead.
So in summary:
Tell the system about your background modes through .plist (Required background modes > App registers for location updates)
Tell the location manager you want to receive updates in the background (self.locationManager.allowsBackgroundLocationUpdates = YES;)
Ask for permission from the user from instance variable
([self.locationManager requestAlwaysAuthorization])
Provide valid texts for this request (info.plist contains: NSLocationAlwaysUsageDescription, NSLocationWhenInUseUsageDescription, NSLocationAlwaysAndWhenInUseUsageDescription)
recently, IOS 13, * save result request permission after user reinstall app
I'm trying to use MapKit on iOS 8 and I keep getting the error:
Trying to start MapKit location updates without prompting for location authorization. Must call
-[CLLocationManager requestWhenInUseAuthorization] or -[CLLocationManager
requestAlwaysAuthorization] first.
Looking it up here, I found that I had to implement NSLocationWhenInUsageDescription in my plist and also make a call to locationManager.requestWhenInUseAuthorization() but nothing happens and I still get that error in the console. What am I doing wrong?
In my application delegate I made an optional var for the locationManager outside the class and then set
locManager = CLLocationManager()
locManager!.requestWhenInUseAuthorization()
This causes the alert view to pop up with your NSLocationWhenInUseUsageDescription or NSLocationAlwaysUsageDescription if you change it appropriately.
Then in the view controller file I made another var outside the class to hold a local CLLocationManager. I then set
if locManager {
locMan = locManager!
locMan!.delegate = self
}
Then you can use the delegate method
func locationManager(_manager: CLLocationManager!,didChangeAuthorizationStatus status: CLAuthorizationStatus)
which gets called when the authorisation status changes, which it does when the user responds to the pop up. Inside this you can use this bit of code to put the user location on the map
if status == CLAuthorizationStatus.AuthorizedWhenInUse {
map.showsUserLocation = true
}
will add the users location to the map only if you are authorised for when in use.
I have been bothered by that one as well, until I realized that the info.plist key has changed. If you had NSLocationUsageDescription in, you will need to change to either NSLocationAlwaysUsageDescription or NSLocationWhenInUseUsageDescription (set at least one to YES), it is now mandatory in iOS8.
And then, Robert's code works, as it should (thanks for sharing).
It is NSLocationWhenInUseUsageDescription, not NSLocationWhenInUsageDescription. Most places online have the wrong key