Hello i am using CLLocation to my app and i have initialise my CLLocationManager like this:
func initLocationManager(){
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters
let authstate = CLLocationManager.authorizationStatus()
if(authstate == CLAuthorizationStatus.NotDetermined || authstate == CLAuthorizationStatus.Denied){
println("Not Authorised")
locationManager.requestWhenInUseAuthorization()
}
locationManager.startUpdatingLocation()
}
And i have also added the NSLocationAlwaysUsageDescription and NSLocationWhenInUseUsageDescription key to my plist.
The first time i open my app i get the prompt message that my app want to access location and it has 2 buttons allow and dont allow. if i click on the dont allow button and close the app, when i opened it again i dont get the prompt message again.
How can i make this prompt message to appear each time the user opens the app? Thank you
Prompting alert each time is not a valid approach.
For an alternative you can show alert only in that case when Location Service is
disabled or "Dont Allow" initially.
Following code promt alert at first and a custom alert when Location Service is Disabled
import UIKit
import CoreLocation
class ViewController: UIViewController,CLLocationManagerDelegate {
var locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
initLocationManager()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func initLocationManager(){
let status = CLLocationManager.authorizationStatus()
if(status == CLAuthorizationStatus.NotDetermined) {
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters
let iosVersion = NSString(string: UIDevice.currentDevice().systemVersion).doubleValue
if iosVersion >= 8.0 {
//For Foreground
locationManager.requestWhenInUseAuthorization()
}
locationManager.startUpdatingLocation()
} else {
if(status != CLAuthorizationStatus.AuthorizedWhenInUse) {
var alert = UIAlertView(title: "Location", message: "Please turn on Location Services", delegate: nil, cancelButtonTitle: "Cancel")
alert.addButtonWithTitle("Open Setting")
alert.show()
/*Add Action on Open Setting alertbutton to directly open settings in iOS 8 and later
-> UIApplication.sharedApplication().openURL(NSURL(string: UIApplicationOpenSettingsURLString)!)*/
}
}
}
}
There is no way of doing it once the user declines, you have to show a dialog explaining to the user that he/she has to go to Settings and manually allow the functionality.
This message is asking for the permission to use GPS of your device and try to getting location of the device that's why GPS is much needed on. For second you don't want to show it you can make a condition with NSUseDefaults and store a key then don't call the method locationmanager startupdatinglocation. This is the only way to don't show it again otherwise it will show everytime.
Related
i'm developing an app. One of the firs thing i do (in AppDelegate) is to invoke OneSignal's initwithlaunchingoptions(...)
This automatically makes my app displays "App wants to send notifications", asking for permissions.
During my app lifecycle, i'll need other permissions from user (like calendar).
I'd like to display (BEFORE all the permissions) a brief AlertView explaining what i'll ask and why.
But how can i accomplish this if i can't move the OneSignal init from AppDelegate while my "explaining alert" happens only in viewDidLoad of the Main ViewController ?
Thanks.
Victor
here is an example of UIViewController that has information about applications needs location data, when user presses UIButton, it asks for permission. you can do alike for all permissions.
class LocationRequestViewController: UIViewController, CLLocationManagerDelegate {
var locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
self.locationManager.delegate = self
}
//when user authorised or denied ->push next `UIViewController`
func locationManager(_: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedWhenInUse || status == .denied {
let destinationVC = self.storyboard!.instantiateViewController(withIdentifier: "Notifications Request")
self.navigationController?.pushViewController(destinationVC, animated: true)
}
}
#IBAction func requestLocation(_: UIButton) {
self.locationManager.requestWhenInUseAuthorization()
}
}
Having some issues using location manager.
It seems like locationManager.requestWhenInUseAuth does not stop app flow, and startUpdatingLocation is called before user can dismiss auth alert.
How to avoid this?
My app loads default values for non-available GPS, so I always get default (because this func is called even if "want to auth this app...?" alert is still showing).
My code:
if ask{
locationManager.requestWhenInUseAuthorization()
self.manageLocation()
}
func manageLocation(){
if CLLocationManager.locationServicesEnabled() {
switch(CLLocationManager.authorizationStatus()) {
case .notDetermined, .restricted, .denied:
// load default deck
self.loadBlink(useDefault: true)
case .authorizedAlways, .authorizedWhenInUse:
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.distanceFilter = 10.0
locationManager.startUpdatingLocation()
}
} else {
// load default deck
self.loadBlink(useDefault: true)
}
}
Requesting authorization in asynchronous. It returns immediately, and you have to take that into account.
Add this into your info.plist
<key>NSLocationAlwaysUsageDescription</key>
<string>$(PRODUCT_NAME) would like to use your location.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>$(PRODUCT_NAME) would like to use your location.</string>
set var locationManager:
var locationManager:CLLocationManager!
on viewDidLoad call this:
locationManager = [[CLLocationManager alloc] init]; // objective-c
locationManager = CLLocationManager() // swift (if i'm not wrong)
call this requestWhenInUseAuth before you call startUpdatingLocation
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.
SITUATION:
I followed the following tutorial:
https://www.raywenderlich.com/95014/geofencing-ios-swift
PROBLEM:
The following functions never get triggered:
AppDelegate.swift
func locationManager(manager: CLLocationManager, didEnterRegion region: CLRegion) {
if region is CLCircularRegion {
handleRegionEvent(region)
}
}
func locationManager(manager: CLLocationManager, didExitRegion region: CLRegion) {
if region is CLCircularRegion {
handleRegionEvent(region)
}
}
func handleRegionEvent(region: CLRegion!) {
print("Geofence triggered!")
// Show an alert if application is active
if UIApplication.sharedApplication().applicationState == .Active {
if let message = notefromRegionIdentifier(region.identifier) {
if let viewController = window?.rootViewController {
showSimpleAlertWithTitle("Congratulations", message: "You just found: " + message , viewController: viewController)
}
}
} else {
// Otherwise present a local notification
let notification = UILocalNotification()
notification.alertBody = "You just found: " + notefromRegionIdentifier(region.identifier)!
notification.soundName = "Default";
UIApplication.sharedApplication().presentLocalNotificationNow(notification)
}
}
QUESTION:
The tutorial was written for iOS 8. I am currently on iOS 9.3. What caused this issue in your opinion and how do I fix it ?
You didn't show the code that you use to set up CL - which is probably where your problem lies.
Did you edit info.plist?
Are you requesting permission?
Did you call one of the start functions on the CL manager?
Make sure of two things :-
1.) You have added These to your viewDidLoad() :-
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.requestWhenInUseAuthorization()
locationManager.startMonitoringSignificantLocationChanges()
locationManager.startUpdatingLocation()
Another alternative to requestWhenInUseAuthorization() and startUpdatingLocation() initialisation in specific to Swift 2.2, since in Swift 2.2 the string literals for selectors is deprecated, and instead there this new operator #selector that you need to be using. :-
you can also use :-
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.startMonitoringSignificantLocationChanges()
if locationManager.respondsToSelector(#selector(locationManager.requestWhenInUseAuthorization)) {
locationManager.requestWhenInUseAuthorization()
}
else {
locationManager.startUpdatingLocation()
}
//Prefer the FIRST ONE.
2.) You have updated your info.plist with :-
NSLocationAlwaysUsageDescription : String :-> I need location.
NSLocationWhenInUseUsageDescription: String :-> I need location.
privacy - location usage description: String :-> I need location.
Edit I need location according to the app's need
PS :- If it still not calls your locationManager functions
Simulator :- look for location settings of your app in your simulator settings.
Device: - Go in settings > Privacy > Location services > Your app > Always.
you also might find this explanation useful : - https://stackoverflow.com/a/26090094/6297658
initialize your location manager in app delegate on did finish launching
my viewController looks like following
import CoreLocation
class MyViewController: UIViewController {
let locationManager = CLLocationManager()
override func viewDidAppear(animated: Bool) {
if #available(iOS 8.0, *) {
self.locationManager.requestWhenInUseAuthorization()
} else {
// Fallback on earlier versions
}
if (CLLocationManager.locationServicesEnabled()) {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
let lon = locationManager.location!.coordinate.longitude
let lat = locationManager.location!.coordinate.latitude
print("lat = \(lat) and long = \(lon)")
}
}
}
// MARK: - CLLocationManagerDelegate
extension MyViewController : CLLocationManagerDelegate {
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
self.locationManager.stopUpdatingLocation()
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
print("\(error.localizedDescription)")
}
}
When I execute my program, it prompts a message like
Allow application to access your location while you use the app?
But the Don't Allow and Allow buttons are disabled.
Can someone guide me on where I am going wrong and what I should be doing.
I can manually allow my application to access location services by going to settings. But I would like to know why the buttons are disabled and what should I do to enable it.
note: I have added NSLocationWhenInUseUsageDescription to my info.plist file.
Thanks.
Dt: 29Oct2015
EDIT:
Uninstalled the app and installed again and tried. Now I am able to access the buttons.
Also, I noticed that sometimes the screen goes unresponsive i.e., screen cannot take any input from user. I noticed it today with a textbox. I am not able to get the cursor to the text box.
Is it something to do with IOS update? is anyone else experiencing this type of weird behaviour. Is there any workaround for the same?
Any help is highly appreciated.
Thanks.