Problems with CoreLocation - swift

For some reason I cannot seem to get the users location. I bought the iOS 9 CookBook and coped their code exactly
Here is the ViewController:
import UIKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
lazy var locationManager: CLLocationManager = {
let m = CLLocationManager()
m.delegate = self
m.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
return m
}()
func locationManager(manager: CLLocationManager,
didUpdateLocations locations: [CLLocation]) {
//TODO: now you have access to the location. do your work
}
func locationManager(manager: CLLocationManager,
didFailWithError error: NSError) {
//TODO: handle the error
}
func locationManager(manager: CLLocationManager,
didChangeAuthorizationStatus status: CLAuthorizationStatus) {
if case .AuthorizedWhenInUse = status{
manager.requestLocation()
} else {
//TODO: we didn't get access, handle this
print("No Access Granted")
}
}
#IBAction func requestLocation() {
locationManager.requestWhenInUseAuthorization()
}
}
The output in the console is:
No Access Granted
Any ideas what is going on? I have tested this on both the simulator and the device
Edit: I have added a button to the storyboard and licked that to the IBAction accordingly.

Have you added the NSLocationWhenInUseUsageDescription key to your plist? - The value should be a string describing why you want to use the users location.
<key>NSLocationWhenInUseUsageDescription</key>
<string>Required to provide information about your location.</string>
If its all working correctly the didUpdateLocations delegate method should return an array of locations, you can print these out like so...
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
for loc in locations {
print(loc)
}
}
If you want to continually monitor the location you can call manager.startUpdatingLocation() instead of manager.requestLocation()

Related

How to display information about my app when I use location and background execution

I use location collection, I want to achieve the effect of the following images, when the collection location is executed and executed in the background, you can display your own app in the status column.
I thought that as soon as I used the location and background, he would automatically display it. But not as I thought.
The following is my code
import UIKit
import CoreLocation
class ViewController: UIViewController , CLLocationManagerDelegate{
var locationMgr : CLLocationManager!
override func viewDidLoad() {
super.viewDidLoad()
locationMgr = CLLocationManager()
locationMgr.delegate = self
locationMgr.desiredAccuracy = kCLLocationAccuracyBestForNavigation
locationMgr.allowsBackgroundLocationUpdates = true
locationMgr.pausesLocationUpdatesAutomatically = true
if CLLocationManager.authorizationStatus() != .authorizedAlways {
locationMgr.requestAlwaysAuthorization()
}else{
locationMgr.startUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let a = locations.last{
print("----location update----")
print(a.coordinate.latitude);
print(a.coordinate.longitude);
print("---------------")
}
}
}
I can get the updated data when I execute it, but I don't see the display of the status bar above.
I don't know what I missed?
The alert on the top shows when you app has permission to access the location when it is in foreground and uses when the app is in background.
Use
if CLLocationManager.authorizationStatus() != .authorizedWhenInUse {
locationMgr.requestWhenInUseAuthorization()
}else{
locationMgr.startUpdatingLocation()
}
instead of
if CLLocationManager.authorizationStatus() != .authorizedAlways {
locationMgr.requestAlwaysAuthorization()
}else{
locationMgr.startUpdatingLocation()
}

CoreLocation in Swift Playground

I am trying to retrieve the current location of a user but in Swift Playgrounds. I have this code already but for some reason it never executes the print statements. Could anyone help?
import UIKit
import CoreLocation
import PlaygroundSupport
var str = "Hello, playground"
let locationManager = CLLocationManager()
PlaygroundPage.current.needsIndefiniteExecution = true
class GeoCoordinateDelegate: NSObject, CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations[0]
print("Most Recent Location: " + location.description)
print(location.coordinate.latitude)
print(location.coordinate.longitude)
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Error while updating " + error.localizedDescription)
}
}
let geoCoordinateDelegate = GeoCoordinateDelegate()
locationManager.delegate = geoCoordinateDelegate
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
When writing CoreLocation code, it is good practice to check CLLocationManager.locationServicesEnabled() before trying to perform any location related actions.
In Swift Playgrounds (1.2 from 2017-03-21), that method returns false as CoreLocation is not available for playgrounds (yet?). I suggest to file a bug report at https://bugreport.apple.com/

How do I display the altitude of the user in swift?

I'm trying to build an app that displays the altitude of the user, but xCode says that CLLocation doesn't have member 'altitude'. The code I'm using is
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { self.Altitude.text = String(locations.altitude)}
Xcode does not say "CLLocation" has no member 'altitude'" but "[CLLocation] has no member 'altitude'".
locations is an array of CLLocation objects and (as it is an array) does not have a property named altitude.
What you need to do is extract one value from the array and use that to access the altitude.
You probably want to do something like this:
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let altitude = locations.last?.altitude else { return }
self.Altitude.text = String(altitude)
}
Since this is a user facing string you should format it properly using a LengthFormatter.
A small style note: variable names (like self.Altitude should be lowercased, only types are PascalCase).
You can get Altitude from reverse geocoding in swift for this you can use locationManager and locationManager didUpdateLocations method.
Step 1: Add below Code in ViewdidLoad method:-
let locationManager = CLLocationManager()
locationManager.headingFilter = 1
locationManager.delegate = self
locationManager.startUpdatingHeading()
Step 2: Use didUpdateLocations Method:-
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let altitude = locations.last?.altitude else { return }
}

Why does LocationManager calls startUpdatingLocation multiple times?

Why is the Location Manager calling startUpdatingLocation more than once? Sometimes it is calling once, other times it is calling it three times. I don't know why; maybe you could help me. I have this code from GitHub.
import UIKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate
{
let locationManager = CLLocationManager()
override func viewDidLoad()
{
super.viewDidLoad()
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
CLGeocoder().reverseGeocodeLocation(manager.location!, completionHandler: {(placemarks, error) -> Void in
if (error != nil)
{
print("Error: " + error!.localizedDescription)
return
}
if placemarks!.count > 0 {
if let pm = placemarks?.first {
self.displayLocationInfo(pm)
}
}
else
{
print("Error with the data.")
}
})
}
func displayLocationInfo(placemark: CLPlacemark)
{
self.locationManager.stopUpdatingLocation()
print(placemark.locality)
print(placemark.postalCode)
print(placemark.administrativeArea)
print(placemark.country)
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError)
{
print("Error: " + error.localizedDescription)
}
}
Yes, this is standard behavior. When you start location services you will generally receive a series of increasingly accurate CLLocation updates (i.e. with horizontalAccuracy decreasing over time) as the device "warms up". For example, it might start reporting location information that it might already have on the basis of cell towers, but as the GPS chip gets more information by which it can better triangulate your location, it will give you updates. Etc.
If you want to reduce this behavior, you can use a combination of a larger distanceFilter, a lower desiredAccuracy, or call stopUpdatingLocation once you get a location that you will geocode.
Right now you are calling stopUpdatingLocation, but you're doing it from the asynchronously called closure of reverseGeocodeLocation. This means that more location updates are able to slip in before the completion handler of reverseGeocodeLocation is called. If you call stopUpdatingLocation synchronously (e.g. before reverseGeocodeLocation), then you will avoid this behavior.

CLAuthorizationStatus AuthorizedWhenInUse called only for the first time

Here's a problem if I don't request "AuthorizedWhenInUse" status once my app first view controller is loaded I'll never get update after.
Let's say I have a map view controller. When I ask for status in the viewDidLoad method it updates my location, i.e. func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) is called.
Now I added an intro view controller, once user finishes intro he/she is going to the old map controller. But now func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) isn't called anymore!
What I noticed is if I go to settings and toggle authorization status manually for my application and then get back to my app the didUpdateLocations is called.
class MapViewController: UIViewController, CLLocationManagerDelegate {
private let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.distanceFilter = 100
locationManager.delegate = self
// >=iOS8
if (locationManager.respondsToSelector(Selector("requestWhenInUseAuthorization"))) {
locationManager.requestWhenInUseAuthorization()
} else {
locationManager.startUpdatingLocation()
}
}
// MARK: - CLLocationManagerDelegate
func locationManager(manager: CLLocationManager!, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
if status == .AuthorizedAlways || status == .AuthorizedWhenInUse {
manager.startUpdatingLocation()
}
}
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
if let location = locations.first as? CLLocation {
println("User's location: \(location.description)")
} else {
println("User's location is unknown")
}
}
}
I see several issues with what you posted, here are just two:
You are calling locationManager.startUpdatingLocation() from viewDidLoad this will only fire if the view is not already loaded into memory. Recommend moving this into viewWillAppear so it fires every time.
This code should be rewritten:
if (locationManager.respondsToSelector(Selector("requestWhenInUseAuthorization"))) {
locationManager.requestWhenInUseAuthorization()
} else {
locationManager.startUpdatingLocation()
}
You need to check the authorizationStatus instead, if it is kCLAuthorizationStatusNotDetermined then request permission using the if statement you have above. If you don't iOS 8 users will always drop into the requestWhenInUseAuthorization section. You don't want that because the OS will only ask for permission once. It will not ask again unless you rest your phones Location Privacy under Settings.