Im having a trouble to add locationManager.requestWhenInUseAuthorization()
What i expect : popup asking for location permission to user
What heppend : nothing show
Setting : location setting in iphone : never
Plist:
LocationAlwaysAndWhenInuseUsageDescription
LocationAlwaysUsageDescription
LocationWhenInUseUsageDescription
import Foundation
import UIKit
import CoreLocation
class NoobNoobVC: BaseVC, CLLocationManagerDelegate {
var locationManager: CLLocationManager!
override func viewDidLoad() {
super.viewDidLoad()
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.distanceFilter = 5
locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
//locationManager.startUpdatingHeading()
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
locationManager.requestWhenInUseAuthorization()
}
}
Documentation says:
You may call requestWhenInUseAuthorization() whenever the current authorization status is not determined (CLAuthorizationStatus.notDetermined).
&
If the initial authorization status is anything other than CLAuthorizationStatus.notDetermined, this method does nothing and doesn't call the locationManager(_:didChangeAuthorization:) method.
You have to use authorizationStatus() to check the authorization status and then take a proper action like - inform the user what's wrong, ... and what can be done about it. You can call requestWhenInUseAuthorization() only if it's .notDetermined. This class function is marked as deprecated and if you're targeting iOS >= 14.0 you should use the instance variant.
Related
How do you get user location permissions in SwiftUI?
I tried asking for user location permissions after a button tap, but the dialogue box disappears after about a second. Even if you do end up clicking it in time, permission is still denied.
import CoreLocation
.
.
.
Button(action: {
let locationManager = CLLocationManager()
locationManager.requestAlwaysAuthorization()
locationManager.requestWhenInUseAuthorization()
}) {
Image("button_image")
}
Things like location manager should be in your model, not your view.
You can then invoke a function on your model to request location permission.
The problem with what you are doing now is that your CLLocationManager gets released as soon as the closure is done. The permission request methods execute asynchronously so the closure ends very quickly.
When the location manager instance is released the permission dialog disappears.
A location model could look something like this:
class LocationModel: NSObject, ObservableObject {
private let locationManager = CLLocationManager()
#Published var authorisationStatus: CLAuthorizationStatus = .notDetermined
override init() {
super.init()
self.locationManager.delegate = self
}
public func requestAuthorisation(always: Bool = false) {
if always {
self.locationManager.requestAlwaysAuthorization()
} else {
self.locationManager.requestWhenInUseAuthorization()
}
}
}
extension LocationModel: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
self.authorisationStatus = status
}
}
You would probably also want functions to start & stop location updates and an #Published CLLocation property
i got some problems with this whole UIViewController thing. My thoughts were, that the viewDidLoad() would be something like a main() in other languages, but especially in this case I don't see any functions called in the viewDidLoad() func.
First of all I am totally confused by the var locationManager which is actually a CLLocationManager and a func at the same time. How?
Where do I call the func locationManager? Can I return the locValue.latitude and the locValue.longitude? How do I catch them in the viewDidLoad()? Finally I want to send these two parameters to something, after I pressed a button (see: func SendButtonAction).
But my problem is, that I don't know how to bring these two guys from the body of func locationManager to an input in func SendButtonAction.
Appreciate any help :) I guess I need more basic knowledge.
import UIKit
import MapKit
import CoreLocation
class GPSNew: UIViewController, CLLocationManagerDelegate {
#IBOutlet weak var mapView: MKMapView!
#IBOutlet weak var zurueckButton: UIButton!
#IBOutlet weak var SendButton: UIButton!
var locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
// Ask for Authorisation from the User.
self.locationManager.requestAlwaysAuthorization()
// For use in foreground
self.locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
}
// Do any additional setup after loading the view.
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
//guard let locValue: CLLocationCoordinate2D = manager.location?.coordinate else { return }
let locValue: CLLocationCoordinate2D = manager.location!.coordinate
print("locations = \(locValue.latitude) \(locValue.longitude)")
let userLocation = locations.last
let viewRegion = MKCoordinateRegion(center: (userLocation?.coordinate)!, latitudinalMeters: 600, longitudinalMeters: 600)
self.mapView.setRegion(viewRegion, animated: true)
//return (locValue.latitude, locValue.longitude)
}
#IBAction func SendButtonAction(_ sender: Any) {
//send the user location to something
//end updating location
locationManager.stopUpdatingLocation()
}
}
Actually the method that gives the location is asynchronous so you can try
#IBAction func sendButtonAction(_ sender: Any) {
if let loc = locationManager.location?.coordinate {
print(loc.latitude, loc.longitude)
locationManager.stopUpdatingLocation()
}
}
From your question it sounds like you're used to procedural programming. In a C command-line utility, for example, you have a main() function that gets called at the launch-time. Main calls other functions to do setup, then might have a loop that steps through the work it has to do, and then either loops, waiting for input from the user, or returns if it's a "one-and-done" utility.
Apps for most (all?) GUI-based OS'es don't work that way. They are event-driven, and usually use an object-oriented design. You should read up on event-driven development. Until you study it, you're going to be very confused and won't be able to get off of square one.
Here is a short intro to the concepts, but this is a much deeper topic than we can cover in a simple post:
In an object-oriented,event-driven program, you create objects that have methods (functions) that respond to things that happen. Your program defines a set of starting objects, and then those objects wait for stuff to happen.
The method viewDidLoad() is an example of a method that gets called when something happens. It gets called when a view controller's (an object that manages a view) view gets created. It gives you a chance to do one-time setup to get ready for the user to "do stuff." Your viewDidLoad() function does that one-time setup, and then returns.
Control then returns to the system, and your app just waits to get called again.
You might also add methods that respond to the user tapping on buttons, sliding, notifications about updated GPS locations, etc.
The location manager (CLLocationManager) is an object that you create when you want to get information about the device's location. You create one, and ask it to notify you about various types of location events. You set up an object to be the location manager's "delegate". This is like giving the location manager a phone number and saying "call this number when the user's location changes."
The location manager calls its delegate when events occur that you told it you care about.
I am following Google Places API for IOS tutorial to view the user current place.
I used the same code in the tutorial as follow:
var placesClient: GMSPlacesClient!
// Add a pair of UILabels in Interface Builder, and connect the outlets to these variables.
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var addressLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
placesClient = GMSPlacesClient.shared()
}
// Add a UIButton in Interface Builder, and connect the action to this function.
#IBAction func getCurrentPlace(_ sender: UIButton) {
placesClient.currentPlace(callback: { (placeLikelihoodList, error) -> Void in
if let error = error {
print("Pick Place error: \(error.localizedDescription)")
return
}
self.nameLabel.text = "No current place"
self.addressLabel.text = ""
if let placeLikelihoodList = placeLikelihoodList {
let place = placeLikelihoodList.likelihoods.first?.place
if let place = place {
self.nameLabel.text = place.name
self.addressLabel.text = place.formattedAddress?.components(separatedBy: ", ")
.joined(separator: "\n")
}
}
})
}
But I get the following error in the console:
Pick Place error: The operation couldn’t be completed. The Places API
could not find the user's location. This may be because the user has
not allowed the application to access location information.
NOTE: I have set the NSLocationWhenInUseUsageDescription key (Privacy - Location When In Use Usage Description) in info.plist file.
It's confusing because I followed the tutorial step by step. And am testing the application using physical device with "Locations Services" enabled .
Any idea what I might be doing wrong?
Or is it because the documentation is not up-to-date?
This may be because the user has not allowed the application to access location information.
This points you towards your answer. For Google Places to work you need to request to use location services by calling requestWhenInUseAuthorization(). This will prompts the user to grant permission to the app to use location services.
Please refer to the Apple Docs for more info.
EDIT
You should keep a strong reference to the CLLocationManager that you create so it does not get deallocated when your function exits.
"Create an instance of the CLLocationManager class and store a strong reference to it somewhere in your app.
Keeping a strong reference to the location manager object is required until all tasks involving that object are complete. Because most location manager tasks run asynchronously, storing your location manager in a local variable is insufficient."
Taken from the CLLocationManager Docs
EXAMPLE
class LocationViewController: UIViewController, CLLocationManagerDelegate {
let locationManager = CLLocationManager()
override func viewDidLoad()
{
super.viewDidLoad()
locationManager.delegate = self
if CLLocationManager.authorizationStatus() == .notDetermined
{
locationManager.requestWhenInUseAuthorization()
}
}
}
1. Request user for Location Usage Authorization
requestWhenInUseAuthorization() OR requestAlwaysAuthorization() according to your requirement.
2. In Info.plist, add the following keys:
a. NSLocationAlwaysUsageDescription OR NSLocationWhenInUseUsageDescription
b. NSLocationAlwaysAndWhenInUseUsageDescription
Example:
class ViewController: UIViewController, CLLocationManagerDelegate
{
let locationManager = CLLocationManager()
override func viewDidLoad()
{
super.viewDidLoad()
locationManager.requestWhenInUseAuthorization()
locationManager.delegate = self
locationManager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus)
{
}
}
I am working on an app which will display points of interest on a map around the users location. The problem is, when the app loads, the permission dialog disappears instantly before the user can allow or deny permissions.
My code looks like this:
override func viewDidLoad() {
super.viewDidLoad()
logo.animation = "zoomIn"
logo.duration = 1
logo.delay = 0.5
logo.animate()
formatView()
let locationManager = CLLocationManager()
let authStatus: CLAuthorizationStatus = CLLocationManager.authorizationStatus()
if authStatus == .notDetermined {
locationManager.requestWhenInUseAuthorization()
}
if authStatus == .denied || authStatus == .restricted {
showLocationServicesDeniedAlert()
return
}
}
But I have no idea what I'm doing wrong. Ive tried to follow other answers on here for similar problems but it seems like there are a few reasons this can happen and I have no idea which one my problem is I'm tearing hairs out!
You are assigning locationManager to a local variable of viewDidLoad which gets released immediately when viewDidLoad finishes. Instead, make locationManager a property of your ViewController:
var locationManager: CLLocationManager?
override func viewDidLoad() {
...
locationManager = CLLocationManager()
...
}
Try moving the the code in viewWillAppear as requestWhenInUseAuthorization needs to be called when view controller has appeared. And you could study the View Controller Life Cycle here to know more about their appearance, loading and everything.
I am trying to get my app to request the user to allow location services when using the app,
I have imported the following in my ViewController.swift
import CoreData
import Foundation
import CoreLocation
and also have the following code in my ViewController.swift
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let manager = CLLocationManager()
if CLLocationManager.locationServicesEnabled() {
manager.startUpdatingLocation()
}
if CLLocationManager.authorizationStatus() == .NotDetermined {
manager.requestWhenInUseAuthorization()
}
in addition after doing a bunch of research I found that I had to add the following to my info.plist
<key>NSLocationUsageDescription</key>
<string>AppName needs to use your location to help you find other blah blah blah near you</string>
Despite taking these steps when running the app in the simulator I don't get a prompt requesting access when using the app
Insert below key in your Info.plist
NSLocationWhenInUseUsageDescription