Swift - User Location Issues - ios

I've just started with swift and I'm having an issue. I've read the various threads about user location and map kits and can't solve my issue. I had the code running and could create regions as I wanted and I could zoom into the user location.
I've paired the code back to try and locate the issue and the code left is below. The issue is that the userlocation is coming back as a nil value when you try and run the simulator which crashes the app. What am I doing wrong as I've completed authorising user location so surely it shouldn't be coming back nil. At one point I had code to zoom on the user location AFTER initially setting a region elsewhere and calling a function to do the zoom, but if you initially try and call the user location its always nil so you can't initialise the map zooming into where the user is which is what I want.
import UIKit
import MapKit
class MapController: UIViewController, CLLocationManagerDelegate {
#IBOutlet weak var mapView: MKMapView!
// MARK: - location manager to authorize user location for Maps app
var locationManager = CLLocationManager()
func checkLocationAuthorizationStatus() {
if CLLocationManager.authorizationStatus() == .AuthorizedWhenInUse {
mapView.showsUserLocation = true
} else {
locationManager.requestWhenInUseAuthorization()
}
}
override func viewDidLoad() {
super.viewDidLoad()
checkLocationAuthorizationStatus()
var userLocation = locationManager.location
println("\(userLocation.coordinate.latitude)")
println("\(userLocation.coordinate.longitude)")
// Do any additional setup after loading the view.
}
}

Firstly, CLLocationManager updates user location asynchronously. That means that even after you call startUpdatingLocation() your location will be nil until location manager returns with the new location.
Secondly, in your code you are not actually calling this method. If you DO need to be able to store the user location then you should change your code to:
import UIKit
import MapKit
class MapController: UIViewController, CLLocationManagerDelegate {
#IBOutlet weak var mapView: MKMapView!
// MARK: - location manager to authorize user location for Maps app
lazy var locationManager: CLLocationManager = {
var manager = CLLocationManager()
manager.delegate = self
return manager
}()
func checkLocationAuthorizationStatus() {
if CLLocationManager.authorizationStatus() == .AuthorizedWhenInUse {
mapView.showsUserLocation = true
locationManager.startUpdatingLocation()
} else {
locationManager.requestWhenInUseAuthorization()
}
}
override func viewDidLoad() {
super.viewDidLoad()
checkLocationAuthorizationStatus()
//location is nil at this point because location update is
//an asynchronous operation!
//var userLocation = locationManager.location
//println("\(userLocation.coordinate.latitude)")
//println("\(userLocation.coordinate.longitude)")
// Do any additional setup after loading the view.
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.last {
//this is the place where you get the new location
println("\(location.coordinate.latitude)")
println("\(location.coordinate.longitude)")
}
}
There is only one minor thing to note. In the last function I am using an argument locations: [CLLocation]. This is definitely correct in Swift 2.0, but in Swift 1.2 it might be locations: [AnyObject] in which case you have to do a conditional downcast yourself.
Let me know if this works for you

Related

locationManager.location returns nil

i am using swift4.2 and Xcode 10 and i am trying to make iOS app uses location service and it gives me exception: Fatal error: Unexpectedly found nil while unwrapping an Optional value
so i tried to check if location returns nil so i copy my code and print location and it returns null , i simulated location in Xcode from product>scheme>edit scheme>default location and checked location in debug area and it simulated to location i choose any one know the problem?
import CoreLocation
class LocationVC: UIViewController,CLLocationManagerDelegate {
let locationManager = CLLocationManager()
var currentlocation:CLLocation!
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startMonitoringSignificantLocationChanges()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
authorizelocationstates()
}
func authorizelocationstates(){
if CLLocationManager.authorizationStatus() == .authorizedWhenInUse {
currentlocation = locationManager.location
print(currentlocation)
}
else{
locationManager.requestWhenInUseAuthorization()
authorizelocationstates()
}
}
I have run your code and I just added missing key Privacy - Location When In Use Usage Description in info.plist file.
And I have made some changes in your code and get nil value because method getting called multiple time and when user give permission and method again called and going to print location detail but the fact is stilllocationManager variable has not user location data yet.
get location details in locationManager when delegate didUpdateLocations called
I have done some changes in your code:
import UIKit
import CoreLocation
class ViewController: UIViewController,CLLocationManagerDelegate {
var locationManager = CLLocationManager()
var currentlocation:CLLocation!
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// authorizelocationstates()
}
func authorizelocationstates(){
if CLLocationManager.authorizationStatus() == .authorizedWhenInUse {
currentlocation = locationManager.location
print(currentlocation)
}
else{
// Note : This function is overlap permission
// locationManager.requestWhenInUseAuthorization()
// authorizelocationstates()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
locationManager = manager
// Only called when variable have location data
authorizelocationstates()
}
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
// Get Location Permission one time only
locationManager.requestWhenInUseAuthorization()
// Need to update location and get location data in locationManager object with delegate
locationManager.startUpdatingLocation()
locationManager.startMonitoringSignificantLocationChanges()
}
}
Hope it will help you.

CoreLocation Delegate functions do not run until view controller viewDidLoad function is finished

This is more than likely something very easy to solve but I've been at it for some time and can't seem to get at an answer.
I would like to know why a Delegate method for CLLocationManager does not trigger until after the ViewDidLoad function when the CLLocationManager is loaded within the ViewDidLoad function.
I have set my default region to Sydney Australia within my App Scheme and I have encapsulated my locationManager within its own class as follows:
import UIKit
import CoreLocation
/* Class location is a class to track user location and return a location object. */
class usrLocation: NSObject, CLLocationManagerDelegate
{
//MARK: Properties
var locationMgr: CLLocationManager!
var location: CLLocation!
var seenError: Bool = false
//MARK: Public Methods
func startTracking() {
locationMgr = CLLocationManager()
locationMgr.delegate = self
locationMgr.desiredAccuracy = kCLLocationAccuracyBest
locationMgr.requestWhenInUseAuthorization()
locationMgr.startUpdatingLocation()
}
//Return a location object
func getLocation() -> CLLocation {
locationMgr.startUpdatingLocation()
return location!
}
//MARK: CLLocationManagerDelegate
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
locationMgr.stopUpdatingLocation()
if (seenError == false) {
seenError = true
print(error)
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
location = (locations ).last
locationMgr.stopUpdatingLocation()
}
}
I have initialised the class in my ViewController and try to begin tracking my current location in viewDidLoad.
The code looks like this.
override func viewDidLoad() {
var location = usrLocation()
override func viewDidLoad() {
super.viewDidLoad()
// Track location
location.startTracking()
location.getLocation()
//update Label text
sLongitude.text = "\(location.getLocation().coordinate.longitude)"
sLatitude.text = "\(location.getLocation().coordinate.latitude)"
}
getLocation() never returns location as it is always nil as the delegate didUpdateLocations function does not run. Why is this?
I tried to get this working using a completion handler but couldn't so my solution has been to ignore setting the label text via viewDidLoad and to instead update the labels once the location variable has been set. That way viewDidLoad completes, the delegate gets called and the labels get updated.
var location: CLLocation! {
didSet {
//update Label text
sLongitude.text = "\(location.getLocation().coordinate.longitude)"
sLatitude.text = "\(location.getLocation().coordinate.latitude)"
}
}

Magnetic Heading sample code for iOS

Can anyone provide me with a short snippet that will return me the magnetic heading of the iPhone?
I do not want Objective-C please. I need it in Swift.
I have written these lines so far but it does not return me any value:
let locManager = CLLocationManager()
locManager.desiredAccuracy = kCLLocationAccuracyBest
locManager.requestWhenInUseAuthorization()
locManager.startUpdatingLocation()
locManager.startUpdatingHeading()
locManager.headingOrientation = .portrait
locManager.headingFilter = kCLHeadingFilterNone
print(locManager.heading?.trueHeading.binade as Any)
Thanks!
You didn't set the delegate for the location manager. iOS does not update your location right away. Rather, it will call a function provided by your delegate when it has a location / heading update. The reason behind this setup is efficiency. Instead of 10 apps having 10 different location managers competing for time on the GPS hardware, these 10 location managers will request to be notified when the GPS has an update.
Try this:
class ViewController: UIViewController, CLLocationManagerDelegate {
#IBOutlet weak var label: UILabel!
var locManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locManager.desiredAccuracy = kCLLocationAccuracyBest
locManager.requestWhenInUseAuthorization()
locManager.headingOrientation = .portrait
locManager.headingFilter = kCLHeadingFilterNone
locManager.delegate = self // you forgot to set the delegate
locManager.startUpdatingLocation()
locManager.startUpdatingHeading()
}
// MARK: -
// MARK: CLLocationManagerDelegate
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Location Manager failed: \(error)")
}
// Heading readings tend to be widely inaccurate until the system has calibrated itself
// Return true here allows iOS to show a calibration view when iOS wants to improve itself
func locationManagerShouldDisplayHeadingCalibration(_ manager: CLLocationManager) -> Bool {
return true
}
// This function will be called whenever your heading is updated. Since you asked for best
// accuracy, this function will be called a lot of times. Better make it very efficient
func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) {
label.text = "\(newHeading.magneticHeading)"
}
}

My simple map project doesn't get & show my location in simulator

I am using XCode v7.2.1, Simulator v9.2 .
I have a UIViewController which shows a map & is supposed to get my location & show it on map:
import UIKit
import MapKit
class LocationVC: UIViewController, MKMapViewDelegate {
#IBOutlet weak var map: MKMapView!
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
map.delegate = self
}
override func viewDidAppear(animated: Bool) {
if CLLocationManager.authorizationStatus() == .AuthorizedWhenInUse {
map.showsUserLocation = true
} else {
locationManager.requestWhenInUseAuthorization()
}
}
}
I have added the NSLocationWhenInUseUsageDescription in info.plist as shown below:
I have also selected the Debug -> Location -> Custom Location ... and set the longitude & latitude of Helsinki, Finland as shown below:
When I run my app, the map is shown, however it doesn't get my location. Why? (I mean I don't see the blue point in anywhere of the map).
===== UPDATE ====
I also tried this when my app is running, however it doesn't help either.
you are requesting the user's location, but not actually doing anything with the response. become the delegate of the location manager and respond to the authorization change.
this code works for me on 7.2.1 (after selecting "Apple" in Debug -> Location):
import UIKit
import MapKit
class ViewController: UIViewController, CLLocationManagerDelegate {
#IBOutlet weak var map: MKMapView!
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
if CLLocationManager.authorizationStatus() == .AuthorizedWhenInUse {
map.showsUserLocation = true
} else {
locationManager.requestWhenInUseAuthorization()
}
}
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
guard status == .AuthorizedWhenInUse else { print("not enabled"); return }
map.showsUserLocation = true
}
}
I agree with #Casey 's answer,but sometimes you need to do a little more with CLLocationManagerDelegate method.
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.first {
//reset mapView's center in case your custom location was wrong.
map.centerCoordinate = location.coordinate
//mannual call show annotations to avoid some bugs
map.showAnnotations(map.annotations, animated: true)
}
}
you just have to add
locationManager.delegate = self
mapView.showsUserLocation = true

error EXC Bad Instruction trying to get current location from another class?

I had a Massive View Controller and attempting to separate my code into different classes.
I created a class CurrentLocation. In the View Controller I called the google maps method animateToLocation and I get an EXC Bad Instruction. Have done a fair amount of research on proper app architecture but still new and learning through experience. Using google maps for iOS and trying to implement this properly. Is it acceptable to put the updating location in a separate class from the ViewController Then just call the methods I desire to call in the ViewController? I am thinking that I've implemented Model-View-Controller correctly and maybe just inheriting something I should not have. Should be a simple fix just have no idea where to start.
import UIKit
import GoogleMaps
class ViewController: UIViewController, CLLocationManagerDelegate, GMSMapViewDelegate {
#IBOutlet weak var googleMapView: GMSMapView!
let locationManager = CLLocationManager()
let currentLocation = CurrentLocation()
override func viewDidLoad() {
super.viewDidLoad()
currentLocation.trackLocation
}
override func viewDidAppear(animated: Bool)
{
super.viewDidAppear(animated)
if CLLocationManager.locationServicesEnabled() {
googleMapView.myLocationEnabled = true
googleMapView.animateToLocation(currentLocation.coordinate) // EXC Bad Instruction
} else
{
locationManager.requestWhenInUseAuthorization()
}
}
import Foundation
import CoreLocation
import GoogleMaps
class CurrentLocation: NSObject,CLLocationManagerDelegate {
override init()
{
super.init()
}
var locationManager = CLLocationManager()
var location : CLLocation!
var coordinate : CLLocationCoordinate2D!
var latitude : CLLocationDegrees!
var longitude : CLLocationDegrees!
func trackLocation()
{
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
{
if CLLocationManager.locationServicesEnabled() {
location = locations.last
coordinate = CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude)
latitude = coordinate.latitude
longitude = coordinate.longitude
}
}
This error is happening because currentLocation.coordinate is nil and you're accessing it as an implicitly unwrapped optional. Basically, you're trying to access a variable before it has anything in in. You need to initialize a CLLocationManager, ask for permissions, and then start updating the location. Check out this great writeup from NSHipster: Core Location in i​OS 8.

Resources