Why is Swift/Core Location showing my lat/long location as being in San Francisco -- even tho I'm in New York? - ios

Hey everyone — I have written some code that identifies location via CLLocation, then converts that lat/long value to a city with CLGeoCoder().
It's working in that the code runs and id's a location -- except I am in New York, and it keeps identifying me as being in San Francisco!
Can you spot any clear mistakes I'm making in the code below? Thank you!
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations[locations.count - 1]
if location.horizontalAccuracy > 0 {
locationManager.stopUpdatingLocation()
locationManager.delegate = nil
let geoCoder = CLGeocoder()
geoCoder.reverseGeocodeLocation(location, completionHandler:
{
placemarks, error -> Void in
guard let placeMark = placemarks?.first else { return }
if let locationName = placeMark.location {
print(locationName)
}
if let street = placeMark.thoroughfare {
print(street)
}
if let city = placeMark.subAdministrativeArea {
print(city)
}
if let zip = placeMark.isoCountryCode {
print(zip)
}
if let country = placeMark.country {
print(country)
}
})
}
}
}

Run your app on a device (such as an actual iPhone) if you want to use location services to detect your actual location. Testing such code on a Simulator is pretty much pointless.

Related

How to use locationManager() in multiple ViewControllers

I need to get the zipCode and the city in multiple viewControllers.
Here is how I'm currently doing it...
import CoreLocation
let locationManager = CLLocationManager()
class MyViewController: UIViewController, CLLocationManagerDelegate{
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
CLGeocoder().reverseGeocodeLocation(manager.location!, completionHandler: {(placemarks, error)-> Void in
if error != nil {
//AlertView to show the ERROR message
}
if placemarks!.count > 0 {
let placemark = placemarks![0]
self.locationManager.stopUpdatingLocation()
let zipCode = placemark.postalCode ?? ""
let city:String = placemark.locality ?? ""
// Do something with zipCode
// Do something with city
}else{
print("No placemarks found.")
}
})
}
func someFunction() {
locationManager.startUpdatingLocation()
}
Everything works fine but as you can see doing it this way in multiple viewController leads to a lot of code repetition (of course, I'm not showing the whole code).
What would be the most common way to retrieve the zipCode and city from CLLocationManager() in a more practical way from multiple viewControllers?
What I'm thinking is something like...
MyLocationManager.zipCode() // returns zipCode as a string
MyLocationManager.city() // returns city as a string
The usual thing is to have just one location manager in one persistent place that you can always get to from anywhere, like the app delegate or the root view controller.
I tried to implement a singleton CLLocationManager class, I think you can modify the following class to implement some additional methods.
import Foundation
class LocationSingleton: NSObject, CLLocationManagerDelegate {
private let locationManager = CLLocationManager()
private var latitude = 0.0
private var longitude = 0.0
static let shared = LocationSingleton()
private override init() {
super.init()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = kCLLocationAccuracyHundredMeters
locationManager.requestAlwaysAuthorization() // you might replace this with whenInuse
locationManager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.last {
latitude = location.coordinate.latitude
longitude = location.coordinate.longitude
}
}
private func getLatitude() -> CLLocationDegrees {
return latitude
}
private func getLongitude() -> CLLocationDegrees {
return longitude
}
private func zipCode() {
// I think you can figure way out to implemet this method
}
private func city() {
// I think you can figure way out to implemet this method
}
}

Why userLocation returns (-180.0,-180.0) coordinates on Mapbox?

I use Mapbox with Swift 4 and I have a problem when I want to display the user location. I don't understand why the user location is not set as it should be.
I would get the user location coordinates in the viewDidLoad() method. To do so, I have set MGLMapViewDelegate and CLLocationManagerDelegate in my ViewController declaration. Then, in my viewDidLoad() I have:
// Mapview configuration
let mapView = MGLMapView(frame: self.mapView.bounds)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
mapView.showsUserLocation = true
mapView.setUserTrackingMode(.follow, animated: true)
mapView.delegate = self
self.mapView.addSubview(mapView)
// User location
print("User location:")
print(mapView.userLocation!.coordinate)
But I get this:
CLLocationCoordinate2D(latitude: -180.0, longitude: -180.0)
I think it is because the location is not set when the view loads, but I need to get values in viewDidLoad().
What should I do, and why the line mapView.userLocation!.coordinate doesn't work?
EDIT
In fact, I want to use MapboxDirections to display on the map a line between the user location and a fixed point. To do it, I use this code (see the first comment):
let waypoints = [
// HERE I would use the user location coordinates for my first Waypoint
Waypoint(coordinate: CLLocationCoordinate2D(latitude: 38.9131752, longitude: -77.0324047), name: "Mapbox"),
Waypoint(coordinate: CLLocationCoordinate2D(latitude: 38.8977, longitude: -77.0365), name: "White House"),
]
let options = RouteOptions(waypoints: waypoints, profileIdentifier: .automobileAvoidingTraffic)
options.includesSteps = true
_ = directions.calculate(options) { (waypoints, routes, error) in
guard error == nil else {
print("Error calculating directions: \(error!)")
return
}
if let route = routes?.first, let leg = route.legs.first {
print("Route via \(leg):")
let distanceFormatter = LengthFormatter()
let formattedDistance = distanceFormatter.string(fromMeters: route.distance)
let travelTimeFormatter = DateComponentsFormatter()
travelTimeFormatter.unitsStyle = .short
let formattedTravelTime = travelTimeFormatter.string(from: route.expectedTravelTime)
print("Distance: \(formattedDistance); ETA: \(formattedTravelTime!)")
if route.coordinateCount > 0 {
// Convert the route’s coordinates into a polyline.
var routeCoordinates = route.coordinates!
let routeLine = MGLPolyline(coordinates: &routeCoordinates, count: route.coordinateCount)
// Add the polyline to the map and fit the viewport to the polyline.
mapView.addAnnotation(routeLine)
mapView.setVisibleCoordinates(&routeCoordinates, count: route.coordinateCount, edgePadding: .zero, animated: true)
}
}
}
Larme is correct: the user's location typically isn't available yet in -viewDidLoad. Use the -mapView:didUpdateUserLocation: delegate method to be notified when the user's location becomes available and when it updates.
If you need the user’s location before a map is shown, consider running your own CLLocationManager.
-180, -180 is the kCLLocationCoordinate2DInvalid constant from Core Location. You should typically check if CLLocationCoordinate2DIsValid() before trying to display CLLocationCoordinate2D on a map.
Sergey Kargopolov has a great example of how to obtain the user location using CLLocationManager and CLLocationManagerDelegate. Here is his code:
class ViewController: UIViewController, CLLocationManagerDelegate {
var locationManager:CLLocationManager!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
determineMyCurrentLocation()
}
func determineMyCurrentLocation() {
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.startUpdatingLocation()
//locationManager.startUpdatingHeading()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let userLocation:CLLocation = locations[0] as CLLocation
// Call stopUpdatingLocation() to stop listening for location updates,
// other wise this function will be called every time when user location changes.
// manager.stopUpdatingLocation()
print("user latitude = \(userLocation.coordinate.latitude)")
print("user longitude = \(userLocation.coordinate.longitude)")
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error)
{
print("Error \(error)")
}
}

how i can take variable from function for another function SWIFT 3

I wont print in console currentCity, for the use city name in another function! Please Help me.
var locationManager = CLLocationManager()
var currentCity: String?
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
print(currentCity) //----error not printing CITY NAME
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let userLocation: CLLocation = locations[0]
CLGeocoder().reverseGeocodeLocation(userLocation) { (placemarks, error) in
if error != nil {
print(error?.localizedDescription as Any)
} else {
if let placemark = placemarks?[0] {
if let city = placemark.addressDictionary?["City"] as? NSString {
self.cityLbl.text = city as String
self.currentCity = city as String
}
}
}
}
}
Location manager is an asynchronous function. Which means your code will initiate the function but carry on with the next piece of code, and not wait to get the location, which will eventually finish later.
So even though you are calling your update location before the print function, by the time the code is executing the print it has not gotten the location yet and your current city is nil.
Print the name from the location manager function or add a completion closure. That should do the trick.
You need to print city in didUpdateLocations.
if let city = placemark.addressDictionary?["City"]
as? NSString {
self.cityLbl.text = city as String
self.currentCity = city as String
print(currentCity) // here you get string
// call that function here
self.myfunction(cityName: currentCity)
}
Function
func myfunction(cityName : String) -> Void {
// your string with city name
}
func viewDidLoad() is called first and at the time the location is not fetched. Try printing after func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) is called.

iOS & Swift: display distance from current location?

I have scoured the interwebs and stackoverflow, and I can't find a solution to my problem.
I am attempting to:
Get a user's current location (lat & long)
Calculate the distance between a user's current location and another location (lat & long) that I set internally
Return the distance in a list view
So far, I can accomplish this if I manually set my current location, but I need to to update.
I have had success returning my current location (I set it as Apple headquarters in the Simulator) in the log, but no success in the actual app or simulator.
Here's what I have:
import UIKit
import CoreLocation
import MapKit
class ViewController: UITableViewController, CLLocationManagerDelegate, MKMapViewDelegate {
override func prefersStatusBarHidden() -> Bool {
return true
}
var shops = [coffeeShop]()
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 = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
}
loadShops()
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let locValue:CLLocationCoordinate2D = manager.location!.coordinate
print("locations = \(locValue.latitude) \(locValue.longitude)")
}
func loadShops() {
let currentLocation = CLLocation()
let currentLat = currentLocation.coordinate.latitude
let currentLong = currentLocation.coordinate.longitude
var myLocation = CLLocation(latitude: currentLat, longitude: currentLong)
let shopLocation1 = CLLocation(latitude: 39.7886939, longitude: -86.1547275)
let distance1 = myLocation.distanceFromLocation(shopLocation1) / 1000
let shop1 = coffeeShop(location: distance1)!
}
In addition, I have everything set in the info.plist and all of that good stuff.
HOW DO I MAKE THIS WORK!? * weeps softly *
Thanks in advance for all of your help!
I was able to use the following code to achieve what I needed. Sometimes you just gotta put it out there in the universe for the universe to respond on its own. Thanks everyone for the help!
let currentLat = self.locationManager.location!.coordinate.latitude
let currentLong = self.locationManager.location!.coordinate.longitude

iOS Simulator not updating location

The below code worked just one time and now all of the sudden it is not showing a point of interest (POI). I tried it on two different machines and am getting the same behavior. This is what has me puzzled, is it the code or my simulator settings.
I cleaned the project, made sure I had the simulator on custom. I did have the longitude and latitude printing now all of a sudden it is not printing in the console either.
import UIKit
import MapKit
import CoreLocation
class MapViewController: UIViewController, CLLocationManagerDelegate {
#IBOutlet var map: MKMapView!
var manager:CLLocationManager!
var latitude:Double = 0.0
var longitude:Double = 0.0
var location:Double = 0.0
override func viewDidLoad() {
super.viewDidLoad()
print("init")
manager = CLLocationManager()
manager.delegate = self
manager.desiredAccuracy - kCLLocationAccuracyBest
manager.requestWhenInUseAuthorization()
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("test test test")
let userLocation:CLLocation = locations[0]
self.latitude = userLocation.coordinate.latitude
self.longitude = userLocation.coordinate.longitude
let request = MKLocalSearchRequest()
request.naturalLanguageQuery = "Army Recruiting"
request.region = MKCoordinateRegionMakeWithDistance(userLocation.coordinate, 1600, 1600)
MKLocalSearch(request: request).startWithCompletionHandler { (response, error) in
guard error == nil else { return }
guard let response = response else { return }
guard response.mapItems.count > 0 else { return }
let randomIndex = Int(arc4random_uniform(UInt32(response.mapItems.count)))
let mapItem = response.mapItems[randomIndex]
mapItem.openInMapsWithLaunchOptions(nil)
}
print("\(self.longitude) \(self.latitude)")
}
----------------------UPDATE 1--------------------
Updating location via simulator
I have noticed the below function is not running:
func locationManager(manager: CLLocationManager, didUpdateLocations
locations: [CLLocation])
---------------------Update 2--------------------
The app worked once then stopped. Not sure what is going on. Yes I added NSLocationWhenInUseUsageDescription and NSLocationAlwaysUsageDescription
Couple of issues with your code:
override func viewDidLoad() {
super.viewDidLoad()
print("init")
manager = CLLocationManager()
manager.delegate = self
manager.desiredAccuracy - kCLLocationAccuracyBest
manager.requestWhenInUseAuthorization()
}
Replace with:
override func viewDidLoad() {
super.viewDidLoad()
print("init")
manager = CLLocationManager()
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
}
ADDITIONALLY (for info)
You can create GPX files for different locations.
You can also simulate routes with in the GPX files - have a delve into the Apple Documentation which shows you hows to set the GPX files to your target build. https://developer.apple.com/library/ios/documentation/IDEs/Conceptual/iOS_Simulator_Guide/CustomizingYourExperienceThroughXcodeSchemes/CustomizingYourExperienceThroughXcodeSchemes.html
There is also a good tutorial here with links for creating GPX files and routes which i have used in the past and is very useful. https://blackpixel.com/writing/2013/05/simulating-locations-with-xcode.html
The simulator can have a set of hard coded locations, and you change them or disable them in the console of xcode.
It should look like this:
If you want to test real moving location you should test it on a device.

Resources