requestAlwaysAuthorization not showing permission alert - ios

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

Related

Strange #NullIsland warning from CoreMotion.framework

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

Disabling Callkit from China Store Best Approach?

We are using CallKit framework to benefit native usage for Voip features. Users can make Voice and Video Calls in our Messenger App.
But Apple removing CallKit apps from China, because of Chinese government.
What is the best approach for CallKit apps like us for now?
We do not want to remove our app from China and we do not remove all CallKit functionality from our app because of China..
I agree with txulu that it seems that CallKit just needs to be disabled/not used for users in China - see this helpful response on the Apple Developer forums.
The general consensus seems to be that as long as you can explain to App Review how you’re disabling CallKit features for users in China, that should probably be acceptable unless/until Apple publishes specific guidelines.
For your particular problem Ahmet, it sounds like CallKit may provide some of the the core functionality of your app. If this is the case and you really need to support users in China, you might want to look at rebuilding your app using another VOIP framework to make calls (VOIP is still allowed in China...just not using CallKit). Or perhaps you could disable and hide the calling features in your app if the user is in China.
My app was only using CallKit to observe when a call initiated from my app ends, so I was able to devise a work around. For users in China I now observe for the UIApplicationDidBecomeActiveNotification and make my best guess about whether a phone call initiated from the app has ended based on how much time has elapsed since the call began. It's not as good as using CallKit's CXCallObserver, but it seems to work well enough for my purpose.
Update! My app passed App Store review with the fix described.
Submitted a new version yesterday.
Included a short message in the reviewer info section saying "In this version and onwards, we do not use CallKit features for users in China. We detect the user's region using NSLocale."
App was approved around 12hr later without any questions or comments from the App Review team.
Detecting users in China
To determine if a user is in China, I am using NSLocale to get the users' currentLocale and countryCode. If the countryCode contains one of the ISO codes for China (CN, CHN), I set a flag to note I cannot use CallKit and not initialize or use CallKit features in my app.
- (void)viewDidLoad {
[super viewDidLoad];
NSLocale *userLocale = [NSLocale currentLocale];
if ([userLocale.countryCode containsString: #"CN"] || [userLocale.countryCode containsString: #"CHN"]) {
NSLog(#"currentLocale is China so we cannot use CallKit.");
self.cannotUseCallKit = YES;
} else {
self.cannotUseCallKit = NO;
// setup CallKit observer
self.callObserver = [[CXCallObserver alloc] init];
[self.callObserver setDelegate:self queue:nil];
}
}
To test this, you can change the region in Settings > General > Language and Region > Region. When I set Region to 'China' but left language set as English, [NSLocale currentLocale] returned "en_CN".
Swift 5
Utility Functions
func isCallKitSupported() -> Bool {
let userLocale = NSLocale.current
guard let regionCode = userLocale.regionCode else { return false }
if regionCode.contains("CN") ||
regionCode.contains("CHN") {
return false
} else {
return true
}
}
MainViewController
class MainViewController: UIViewController {
...
var callObserver = CXCallObserver()
...
override func viewDidLoad() {
super.viewDidLoad()
if isCallKitSupported() {
callObserver.setDelegate(self, queue: nil)
}
...
}
...
}
Note: countryCode is now regionCode and only returns 'US', 'CN', etc. No language before country code like 'en_CN'.
Swift 5
func isCallKitSupport() -> Bool {
let userLocale = NSLocale.current
if userLocale.regionCode?.contains("CN") != nil ||
userLocale.regionCode?.contains("CHN") != nil {
return false
} else {
return true
}
}
One thing you could try, even though it may not work: disable callkit functionality based on the locale region. This may be enough "proof" that Callkit is disabled for China from the legal perspective in order to be approved for the Appstore. Then your Chinese customers could just switch the region in the settings to get Callkit. This would be already "their" problem so to speak.
Disclaimer: I'm by no means a lawyer or anything, follow this advice at your own risk.
Edit:
CXProvider.isSupported is no longer available: I keep the answer here hoping that it will be restored back on an upcoming iOS 13 release.
From iOS 13 onwards, the correct way to do this is to check the new CXProvider.isSupported property.
Here's the documentation (from Xcode, as the online documentation has not been updated yet):
Go to “Pricing and Availability” in iTunes Connect.
Availability” (Click blue button Edit).
Deselect China in the list “Deselect” button.
Click “Done”.

Create local location based notifications in swift

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.

swift user's location issue when trying to get current location

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!

MapKit in iOS 8 and Swift

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

Resources