Stop Returning Cached CLLocationManager Location - iphone

I am wondering if there is some way to make it so CLLocationManager doesn't automatically returned a cached location. I understand that the documents say "The location service returns an initial location as quickly as possible, returning cached information when available" but this cached location could be extremely far away from the user's current location and I would like it more precise if possible.
Thanks for any help!

You can't stop it from caching, but it's not hard to filter out cached data. Core Location includes a timestamp with its locations. Compare the timestamp of the location with a timestamp saved when your app started, and you'll be able to tell which locations are old (cached, found before your app stated) and which are new. Throw away the old ones.
The location timestamp is an NSDate, so just get the value of [NSDate date] when your app starts up and use that as your reference point when filtering locations. You could even throw away the reference value once you start getting new data and treat a nil reference date as implying that new locations should be trusted.

-(void)locationManager:(CLLocationManager*)manager didUpdateToLocation:(CLLocation*)newLocation fromLocation:(CLLocation *)oldLocation
{
if ([newLocation.timestamp timeIntervalSinceNow] > -10.0) // The value is not older than 10 sec.
{
// do something
}
}

You can use this property of CLLocationManager:
#property(readonly, NS_NONATOMIC_IPHONEONLY) CLLocation *location;
The value of this property is nil if no location data has ever been retrieved, otherwise, this is where CoreLocation caches its data. Therefore, if you always want to start from scratch, simply check if this property is nil or not. If it's nil, then you are ok; otherwise, you need to stop your location manager and start it again: this will force an update, which will result in the cached value being overwritten by the fresh one you have just triggered.

Related

Improve Accuracy of Location Manager in iOS Programming

I am making an application which tracks the user. I have notice when the application goes in the background and then when you open the app it's shows the wrong current location for the user until about 5 seconds. Is there to fix that because that 5 seconds delay ruins the tracking results ( it's adds three extra miles for no reason ).
Edit: The issue wasn't actually a "bug". I have to set in my Info.plist that I want background pocessing and boom the application tracking is super Accurate. A little tutorial to do that:
Go to Info.plist
Add an New Row called "Required background modes"
Then add again a new row called "App registers for location updates"
We are Done :)
One thing you can do is check the horizontalAccuracy property on the CLLocation that you're being returned. If this is above a certain threshold then you could throw away the result and wait for a more accurate one. If it is a number of miles out then I would expect the accuracy figure to be quite large. It's most likely using a cell site to determine location rather than GPS, and the margin of error would be much greater.
In your CLLocationManagerDelegate's locationManager:didUpdateLocations: method you could do the following:
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
if ([locations count] > 0) {
CLLocation *lastUpdatedLocation = [locations lastObject];
CLLocationAccuracy desiredAccuracy = 1000; // 1km accuracy
if (lastUpdatedLocation.horizontalAccuracy > desiredAccuracy) {
// This location is inaccurate. Throw it away and wait for the next call to the delegate.
return;
}
// This is where you do something with your location that's accurate enough.
}
}

how to change gps update time in ios

I'm looking for a way to get a background location update every n minutes in my iOS application.. now i am getting update in every second.. can i change one second to three or four second or every minute...
Here is my code:
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
if([self.delegate conformsToProtocol:#protocol(GPSLocationDelegate)]) {
// Check if the class assigning itself as the delegate conforms to our protocol. If not, the message will go nowhere. Not good.
[self.delegate locationUpdate:newLocation];
NSLog(#"location manager");
/// alert for check msg ////
////////////////////////////
}
}
You can not do it.
Alternatively you can achieve it by below way. When you get location updates, it will have the timestamp associated with it.
Cache the time stamp on first location update. When you receive the second location update, compare the difference and check. If it is greater than your threshold limit, call your delegate and update the cached time stamp with updated location's time stamp.
You could stop the location manager when you receive a new location and schedule a timer to start it again 60 seconds past. The only problem will be that you won't get the events triggered exactly after each 60 seconds, but it may be ok.
And also be careful when you stop your location manager, as I think that its first event can be invalid (giving you just a copy of your last location), so may be you will have to keep it running for 5-10 seconds before stopping it and scheduling the next trigger.
Good luck with that!
Location updates are asynchronous, you can't control the frequency in which they appear.

Core Data fetch persistance

I am wondering if someone could clarify the following from Apples Core Data documentation:
Changes are not reflected until after the controller’s managed object
context has received a processPendingChanges message. Therefore, if
you change the value of a managed object’s attribute so that its
location in a fetched results controller’s results set would change,
its index as reported by the controller would typically not change
until the end of the current event cycle (when processPendingChanges
is invoked). For example, the following code fragment would log
“same”:
NSFetchedResultsController *frc = <#A fetched results controller#>;
NSManagedObject *managedObject = <#A managed object in frc's fetchedObjects array#>;
NSIndexPath *beforeIndexPath = [frc indexPathForObject:managedObject];
[managedObject setSortKeyAttribute:
<#A new value that changes managedObject's position in frc's fetchedObjects array#>;
NSIndexPath *afterIndexPath = [frc indexPathForObject:managedObject];
if ([beforeIndexPath compare:afterIndexPath] == NSOrderedSame) {
NSLog(#"same");
}
What exactly does "would typically not change until the end of the current event cycle" mean? I have this situation in my code but am not really sure if I can 100% rely on my indexes staying the same until I explicitely preform a save on my managed object context. Could the above code be modified, without performing a save to the context, such that it doesn't log "same"?
not really sure if I can 100% rely on my indexes staying the same
until I explicitely preform a save on my managed object context.
I wouldn't. The "current event cycle" is the current iteration of the run loop.

issues with geting location coordinate time to time

time to time there is an issue with geting the location coordinate on my app.
I have been testing my app for location coordinate from the simulator and the iphone sitting at my home (it is not just my home, i tested it in different location (outdoor) as well with a very good network connectivity), and i see this wired behavior, i have the right co ordinate at the moment and then i send the app to the background and bring it back i get the right location co ordinate, and if i do it 8-10 times (i.e sending it to background and bringing it to foreground) once in a while after the app comes from the background i cannot get location co ordinate, the only way to get the location co ordinate at this moment is to kill the app and then start fresh. So i am sure some thing is going wrong but I am not sure what is it.
This is what I am doing
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
//if the time interval returned from core location is more than 30 seconds we ignore it because it might be from an old session
if ( abs([newLocation.timestamp timeIntervalSinceDate: [NSDate date]]) < 30) {
if(newLocation.coordinate.latitude != previousLocation.coordinate.latitude && newLocation.coordinate.longitude != previousLocation.coordinate.longitude){
if(newLocation.horizontalAccuracy <= 100){
[self.locationManager stopUpdatingLocation];
[self.locationManager startMonitoringSignificantLocationChanges];
}
else{
[self.locationManager startUpdatingLocation];
}
So basically i take the newLocation only if it is not older then 30 sec, and it is not same as previous location that i have stored locally and the horizontal accuracy is less than 100 meter. When i run it in debugger what i am observing is i get to the first if condition 3-4 times and if it fails it doesn't come after that, which means didUpdateToLocation doesn't get called at all.
Once the co ordinate meet all my criteria I do stopupdatinglocation and do startMonitoringSignificantLocationChanges.
The reason i am doing startUpdatingLocation in my else block is.
For example if didUpdateToLocation got called due to the startMonitoringSignificantLocationChanges, i want to get the accurate location after that so i am doing startUpdatingLocation every time i don't get the right location that i am looking for as i believe doing multiple startUpdatingLocation doesn't harm anything.
Let me know if there is something wrong in my thought process or in the code logic.
The first thing I would check is your info.plist. You have to include the key Required Background Modes -> App registers for location updates. Without this you won't receive location updates (besides significant location changes and region monitoring) while in the background.
In the main else, you call startUpdatingLocation. While I don't believe this hurts anything, unless you are balancing it with a stopUpdatingLocation I don't believe it will do anything. Documentation says:
Calling this method several times in succession does not automatically result in new events being generated. Calling stopUpdatingLocation in between, however, does cause a new initial event to be sent the next time you call this met
But either way, I don't think you need to tell it to startUpdating again, it will continue on it's own when the distanceFilter property is exceeded or the hardware gathers a more accurate location reading.
I don't see a closing } for your second if.

How can I detect whether the iphone has been rebooted since last time app started

I'd like to detect from within my app whether the iPhone has been rebooted since last time my app was started. I need to do this because my app uses the timer since last system reboot to clock a user's time and I want to detect reboot so I can invalidate the time.
Is there anywhere I could extract the information from the system console log like reboot , crashes ? The organizer in xcode can access it , maybe I can too.
If not , can you think of other ways to get this information?
This seems like it would work:
get the time since last reboot, and for this example, let's store it in a variable called 'tslr' (duration in milliseconds I guess, BTW, how do you get that?)
get the current time, store it in variable 'ct' for example
compute the last reboot time (let's call it 'lr'), we have: lr = ct - tslr
store 'lr'
Next time your application gets started, load the previous value for 'lr', compute the new one, and if they differ, you have detected a reboot (you'll probably have to tolerate a small difference there... a couple milliseconds perhaps).
I think it would be pretty tough to fool that... the user would have to tamper their phone time very precisely, and they would have to start your application at a very precise moment on top of that, exactly when the new 'lr' would be identical to the previous one... pretty tough to do, the probability of them being able to do that is very close to 0 I think. And you don't need any internet connection to do that...
The new 'lr' would be identical to the previous one in the following cases only:
phone was not rebooted, and time was not changed
time was tampered with, AND the user managed to start your application at the precise millisecond to fool your algorithm (chances of that happening more than ultraslim)
// Returns true if device has rebooted since last time
private func deviceRebootedSinceLastTime() -> Bool {
let userDefaults = NSUserDefaults.standardUserDefaults()
let systemUptime = NSProcessInfo.processInfo().systemUptime;
let timeNow = NSDate().timeIntervalSince1970
let dateOfLastReboot = NSDate(timeIntervalSince1970: timeNow-systemUptime)
var didDeviceRebootSinceLastTime = false
if let storedDateOfLastReboot:NSDate = userDefaults.objectForKey("deviceLastRebootDate") as? NSDate {
if Int(dateOfLastReboot.timeIntervalSinceDate(storedDateOfLastReboot)) < 1 { //
print("Reboot time didn't change - date: \(dateOfLastReboot)");
}
else {
print("Reboot time has changed - from: \(storedDateOfLastReboot) to \(dateOfLastReboot)");
didDeviceRebootSinceLastTime = true
}
}
else {
print("Reboot time is saved for the first time")
didDeviceRebootSinceLastTime = true // first time we save value
}
userDefaults.setObject(dateOfLastReboot, forKey: "deviceLastRebootDate")
userDefaults.synchronize() // don't forget this!!!
return didDeviceRebootSinceLastTime;
}
Zoran's answer is the right way to go; it's the closest you are going to get without a network connection. (neither the cellular subsystem, nor the syslog are accessible for security reasons)
If you are looking to prevent malicious users from generating fake time data, have some central server (or trusted local server for enterprise deployments) track time-related events for you.
Get and save the time either from the iPhone or from NIST and the current runtime from the BSD uptime function. For NIST time see How can I get the real time in iPhone, not the time set by user in Settings?
When you want to check for a reboot get new values of these, compute the elapsed time for each and compare the elapsed times. Based on the difference you should be able to determine a reboot.
Here is one I made. It takes the current time in GMT and the time since last reboot to extrapolate a date for when the device was last restarted. Then it keeps track of this date in memory using NSUserDefaults. Enjoy!
Note: Since you want to check this since last time app was started, you need to make sure you call the method anytime the app is started. The easiest way would be to call the method below in +(void)initialize { and then also whenever you need to check it manually
#define nowInSeconds CFAbsoluteTimeGetCurrent()//since Jan 1 2001 00:00:00 GMT
#define secondsSinceDeviceRestart ((int)round([[NSProcessInfo processInfo] systemUptime]))
#define storage [NSUserDefaults standardUserDefaults]
#define DISTANCE(__valueOne, __valueTwo) ((((__valueOne)-(__valueTwo))>=0)?((__valueOne)-(__valueTwo)):((__valueTwo)-(__valueOne)))
+(BOOL)didDeviceReset {
static BOOL didDeviceReset;
static dispatch_once_t onceToken;
int currentRestartDate = nowInSeconds-secondsSinceDeviceRestart;
int previousRestartDate = (int)[((NSNumber *)[storage objectForKey:#"previousRestartDate"]) integerValue];
int dateVarianceThreshold = 10;
dispatch_once(&onceToken, ^{
if (!previousRestartDate || DISTANCE(currentRestartDate, previousRestartDate) > dateVarianceThreshold) {
didDeviceReset = YES;
} else {
didDeviceReset = NO;
}
});
[storage setObject:#(currentRestartDate) forKey:#"previousRestartDate"];
[storage synchronize];
return didDeviceReset;
}