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

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.

Related

Make a service call while application is in background state

I have to hit server for every 30 seconds while application is in background state.if I had written code in applicationDidEnterBackground method it is getting called only once but I need to hit server continuously for every 30 seconds while app is in background.
Please use HSLocationManager for your requirement. I have achieved the same requirements in one of my project
Location manager that allows getting background location updates every
n seconds with desired location accuracy.
Advantage:
OS will never kill our app if the location manager is currently
running.
Give periodically location update when it required(range is between 2 -
170 seconds (limited by max allowed background task time))
Customizable location accuracy and time period.
Low memory consumption(Singleton class)
Default time to retrieve location is 30 sec and accuracy is 200.
static let timeInternal = 30
static let accuracy = 200
Update:
Yes, You can do it by writing API call in didUpdateLocations method.
func scheduledLocationManager(_ manager: HSLocationManager, didUpdateLocations locations: [CLLocation]) {
logh("Make API Call here...")
}

CLLocation Manager giving incorrect location sometime

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.

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.

Resources