CLLocation Manager giving incorrect location sometime - ios

I am using core location API for iOS (CLLocation manager) and its sometimes giving me wrong location like if I am in U.S it gives me latitude and longitude of Spain. Searched stack over flow for the same but couldn't get a relevant answer for my query. Being novice to the use maps and locations I am a bit confused. Any help would be highly appreciated.

I assume that you are using a physical device and not the simulator to test this, and you did not select the option to simulate location in xcode. Sometimes CLLocation manager can return for example an old cached location, so perhaps that is what happened to you. Before you do anything with received CLLocation object I recommend checking its horizontalAccuracy and timestamp properties.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let lastLocation = locations.last, lastLocation.horizontalAccuracy < 1000, abs(lastLocation.timestamp.timeIntervalSinceNow) < 60 else {
// location not accurate or to old (cached previously) so do not do anything
return
}
// do something
}

Actually, Apple use wifi devices around for location detection. So on "cold" start gps chip needs some time to start and find satellites, and during that time device use location based on router's mac addresses around. If your (or your neighbor's) router ever been in Spain, than that's the reason - Apple database of mac addresses refresh very slowly, it could take 1-3 months for that.

Related

locationManager(_:didUpdateLocations:) last entry is sometimes extremely outdated

According to Apple's own documentation around CLLocationManager
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
{
locations.last // this is supposed to be the most recent
}
However, during development, I sometimes notice that locations.last is sometimes minutes to hours out of date.
abs(locations.last.timestamp.timeIntervalSinceNow) // sometimes very large (e.g. 500, 5000, etc)
Does anyone have any ideas what might be causing this issue?
When Location Service can't get the current location by requesting GPS server.
It will return the cached/recent location. That is why you see its timestamp is 1 min or 1 hour ago.
It will return the cached/recent location.
This will help users to quickly access their current locations if they didn't travel significantly, that is when Accelerometer works to measure the movement of the device.

Run action when iOS device is near some map point

I have the mobile app that uses user's location when the app is running to show some places on a map (now it is Google Map).
I want to add a new feature - App in the background should notify the user when the device is near some map point or entering the predefined area.
How can it be done? Are there different approaches?
Where to start from?
What if my points stored on the server - should I save them on the device first and what if can't do it and new map points can be added when the app is not in memory?
What will be the most challenging in creating such feature?
p.s. I know about Ray's geofencing tutorial but I want to know - are there any different approaches and also about pitfalls of such feature
First of all you must enable location updates and remote notifications in background modes(Your Target->Capabilities), then you must write in your location update function some code to detect that user is close to some location exapmle:
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let userLocation = locations.first {
if location.coordinate.langtitude <= <some value> ||
location.coordinate.langtitude >= <some value> || ... {
runAction()
}
}
}
func runAction() {
//show local notification here
}

How can I get trueHeading just once in iOS?

As a fresh iOS developer, I am confused to get trueHeading data recently.
class AR: CLLocationManagerDelegate{
var heading:Float!
func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) {
heading = Float(newHeading.trueHeading)
}
}
Via above code, I can continuously get current device's direction. However, can I just get the trueHeading just once? In addition, I tested the trueHeading, it will be accurate after seconds. Can I get the trueHeading at a particular time?
Thank you for any help!
Whenever you need heading you can call
[self.locationManager startUpdatingHeading];
Once you have received the heading, you can do a
[self.locationManager stopUpdatingHeading];
To receive the latest derived heading,
self.locationManager.heading;
So ideally, you call startUpdatingHeading, show a loader or something to the user.
Once you get a heading in didUpdateHeading, relay a message to the controller via a notification or something, and stopupdating heading.
You cannot set the accuracy of the Heading, but you can set the headingFilter to only update when there is a large distance.
/* Notify heading changes when heading is > 5.
* Default value is kCLHeadingFilterNone: all movements are reported.
*/
self.locationManager.headingFilter = 5;
Ideally, you do not stop ever, you continue accessing the heading, with a filter of 5, ensuring it does not drain battery.
You store this in the defaults lets say, and use it whenever needed and update it.
If you need it just for once, then you may do a quick fix by waiting for some duration or lets say some hits to the function, but that is not an ideal solution.
Maybe this can also help, but it is primarily for distance, and not heading. Heading is supposed to be constantly updated.
self.locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
If you want this on app launch, do it in the app delegate.

Which one to use "locations array" or "manager.location.coordinate" in Swift in iOS to get user location?

I want to get the user's location. It might be approximate position, that's fine.
In didUpdateLocations method, i saw two ways to get the coordinates.
Using manager.location.coordinate
Using locations array
Which one should I go for? I am thinking locations array will contain user's recent location as I am starting startUpdatingLocation() as soon as user opens the app.
So which one should i use to get the coordinates? And what does manager.location.coordinate do in this case?
Note: Accuracy of location need not be accurate to 10 meters. Rough estimation is enough.
Here is my code:
func initLocationUpdates() {
if CLLocationManager.locationServicesEnabled() {
print("going to get location")
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
}
else {
print("location services disabled")
}
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let locValue:CLLocationCoordinate2D = manager.location!.coordinate
print("locations = \(locValue.latitude) \(locValue.longitude)")
/*
let userLocation:CLLocation = locations[0] // or maybe even locations.last
let long = userLocation.coordinate.longitude;
let lat = userLocation.coordinate.latitude;
print("lat: ", lat, " --- long: ", long)
*/
locationManager.stopUpdatingLocation()
}
I'd prefer the array over the instance variable, because of the way desiredAccuracy works - it doesn't guarantee that the location updates you receive will have the accuracy you request - merely that the system will make its best effort to provide them. Depending on how the location manager behaves, you might get a number of updates in the locations array, with differing levels of accuracy. You can then filter the array to find the most accurate one.
The key point is that if multiple updates arrive in the same didUpdateLocations: call, and you just use manager.location, you might be missing an update. The missing update would definitely not be the most recent one, but it might be the most accurate one received so far.
Since you have set desiredAccuracy to a very high level (ten meters), I'm guessing precision is more important to you than timing - it will take a while for the system to deliver an update of that level of accuracy (and it may never arrive if you are indoors or otherwise blocked from using GPS). So the likelihood of the scenario above occurring is reasonable.
As for the purpose of the instance variable location, the docs suggest that it's intended to be used in certain circumstances when the app restarts:
In iOS 4.0 and later, this property may contain a more recent location object at launch time. Specifically, if significant location updates are running and your app is terminated, this property is updated with the most recent location data when your app is relaunched (and you create a new location manager object). This location data may be more recent than the last location event processed by your app.

didUpdateToLocation called only twice on 3G

the problem is when i turn internet connection from wifi to 3G, location does not updating and did Update To Location is never called.anyone can help me? i have to send updated current location to server through web service after every 5 seconds.web service is calling but every time the same coordinates goes to server.i want to send coordinates of updated location but on 3G the location update method never called when i change location
[mapview animateToLocation:newLocation.coordinate];
Currentmarkers.position = CLLocationCoordinate2DMake(newLocation.coordinate.latitude,newLocation.coordinate.longitude);
deleg.currentCoordinates = [[CLLocation alloc]initWithLatitude:newLocation.coordinate.latitude longitude:newLocation.coordinate.longitude];
Following is the code which i used to create location manager :
currentLocation = [[CLLocationManager alloc]init];
currentLocation.distanceFilter = kCLDistanceFilterNone;
currentLocation.desiredAccuracy = kCLLocationAccuracyHundredMeters; // 100 m
currentLocation.delegate = self;
[currentLocation startUpdatingLocation];
Switching from WiFi to 3G should not have anything to do with it.
You need to post your code that creates the location manager, configures it, sets you up as the delegate, as well as your location manager delegate methods. If you have the desiredAccuracy or the distanceFilter values set high enough, you won't get notified until you move quite a distance.
Try setting the accuracy to the "best" value, and the distance filter value to kCLDistanceFilterNone for testing.
You may also need to add a scrolling text view to your interface that logs location updates, and then take your device and go for a walk of a kilometer or so. Then you should move var enough to be sure to get location updates.

Resources