How long does CLLocationManager store previous (old) location in cache? - objective-c

Most of the time devs are asking how to filter old cached locations, however I'm interested in opposite thing - how to preserve and get old location :) Here's my case: I have weather screen in my app where I'm downloading weather forecasts for current location. As soon as the app starts, I'm starting standard location updates with 1km accuracy and saving received location in my locator class private member. If the app does not receive any location update then it needs to show any known previous location - like grabbing one out of [CLLocationManager location].
The problem is with iPad2. My app runs without device restart for about a week but [CLLocationManager location] returns nil very frequently. For example, I have opened google maps app yesterday on iPad and got the message "Cannot determine location", and so [CLLocationManager location] returned nil in my app. Then I pressed location arrow icon in google maps app and then google maps app showed my current place. Also, [CLLocationManager location] returned some location in my app. However, the same problem persist today - [CLLocationManager location] returns nil again, so I guess CLLocationManager discards location cache after some time and if you are not moving with device then you will not get any known previous location. Any ideas how to workaround this and to force CLLocationManager to retrieve any location if it's nil?

Documentation
The most recently retrieved user location. (read-only)
#property(readonly, nonatomic) CLLocation *location
Discussion
The value of this property is nil if no location data has ever been retrieved.
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 application is terminated, this property is updated with the most recent location data when your application 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 application.
It is always a good idea to check the timestamp of the location stored in this property. If the receiver is currently gathering location data, but the minimum distance filter is large, the returned location might be relatively old. If it is, you can stop the receiver and start it again to force an update.
It will store it in the cache for as long as possible, there isn't a timeout on it. It may set it to nil if something that used location services failed to get any location.

Related

Getting user's location once, and only once

My app needs to know the rough location of a user, with a resolution of their state/province. This way we know what the default tax rate should be in a simple tax calculator. They can still pick it after that, but its always nice to try to get it right.
Is there an easy way to get this sort of resolution without all the rigamarole of callbacks and such? I don't need updates, a single rough location will do, and an old one is likely perfectly fine.
There are no short simple ways to get current location.
That means that you need init CLLocationManager object, call startUpdatingLocation method and retrieve result in delegate method.
It's impossible to get current location synchronously by using one line of code because retrieving location info could take much time (For example turning on GPS sensor and so on.). That's why you get info by asynchronous delegate methods.
is the only way,
but to get a single user position
within the method locationManager:didUpdateLocations: call stopUpdateLocation:
[locationManager stopUpdatingLocation]

CoreLocation Framework: Does locationManager send a message with filled in parameters everytime it logs a new location?

In the process of learning iOS development and I am currently being taught how to use the core location framework.
I'm told that we need to create an instance of CLLocationManager, and then set a delegate, then implement this method:
-(void) locationManager: (CLLocationManager*)manager
didUpdateToLocation: (CLLocation*)newLocation
fromLocation: (CLLocation*)oldLocation
The book doesn't thoroughly explain how the location is actually received. From what I'm understanding, whenever locationManager logs a new location, it then sends a message (to the delegate?) with the selector being the above method, filling the parameters with the location data? Then we must implement this method and choose what to do with these parameters.
Is this correct? and if not, could someone explain to me exactly what is going on?
Thanks in advance, this is confusing me a ton.
Right, although the message you should implement starting in iOS 6 is -locationManager:didUpdateLocations:. After setting up the delegate, call -startUpdatingLocation and the Location Manager will start sending -locationManager:didUpdateLocations: (or the other method) whenever the location changes until you tell it to stop. Your implementation of that method an do whatever you like -- update a position on a map, log the location to a file, look up the nearest gas stations... There's some reason that you're asking for location updates, and whatever that reason is, this lets you do it.

Should I send [locationManager stopUpdatingLocation] in locationManager:didFailWithError:?

My app uses the typical pattern of starting a CLLocationManager and then calling stopUpdatingLocation from locationManager:didUpdateToLocation:fromLocation: if the newLocation is accurate enough. My question is whether I also need to call
[locationManager stopUpdatingLocation];
in locationManager:didFailWithError:. The Apple docs say
If the location service is unable to retrieve a location right away, it reports a kCLErrorLocationUnknown error and keeps trying. In such a situation, you can simply ignore the error and wait for a new event.
If the user denies your application’s use of the location service, this method reports a kCLErrorDenied error. Upon receiving such an error, you should stop the location service.
In the former case I shouldn’t call stopUpdatingLocation, since the location manager may still emit a good location. What about the other cases? My app always checks [CLLocationManager locationServicesEnabled] and [CLLocationManager authorizationStatus] before trying to use location services, so do I really need to handle the kCLErrorDenied case? And in the event of any other error, will location services be stopped automatically?
First off I think you should handle most error cases. Especially if it's as easy to handle as this one. ;-)
What happens if the user has granted access and uses your app. While the app is running she multitasks and changes into settings.app to disable location services; either generally or specifically for your app. Then you already checked for authorization and use location updates, but suddenly you aren't authorized anymore. I guess that's exactly the case you are asking about, right?

CLLocationManager initialization

I'm working on a web application that enables users to login, only at specific locations. I'm using CLLocationManager and I initialized and called startUpdatingLocation at AppDelegate -didFinishLaunchingWithOptions. My UIWebView is supposed to pull the location at initialization to determine whether user's within the specified locations. However, at the launch of my application, [locationManager location] is always null and updates after my UIWebView is initialized and displayed therefore already sending an error message to the user.
How do I make my locationManager update its location at initialization?
Sounds like you've coded the location stuff correctly. What you are missing (but have seen) is that the update most certainly does not happen instantaneously. You need to "gate" the rest of your UI's presentation (i.e. your webview) on the location information becoming available. There are a lot of ways to do this. A common tactic is to present a full-screen "HUD" or veil with some indicator to the user that the app is initializing or locating them (with an activity indicator, too, is always a nice touch.) Behind that (out of sight to the user) you can be waiting for the location result and then kickoff the appropriate UI update, and then drop the veil.
Any of that make sense or give you some ideas? I've done this plenty of times. Synchronizing async activities (like location updates) with real-time UI updates that make sense can be challenging, but isn't impossible. :-)
You will need to account for an initial nil value in your applications UI and wait for the first location update.
-location
Discussion
The value of this property is nil if no location data has
ever been retrieved.
It is a good idea to check the timestamp of the location that is
returned. If the receiver is currently gathering location data, but
the minimum distance filter is large, the returned location might be
relatively old. If it is, you can stop the receiver and start it again
to force an update
Also you should check out Region Monitoring since you would like for you users to only be able to login at specific locations.

Deleted Entries not being detected - iOS

Is it possible to detect deleted Address Book entries (for example from the iOS phonebook itself) from within an application?
I have an application which needs to synchronize a local copy of the iOS Address Book database with the Address book from the phone. The application supports background mode on iOSes which support it.
Testing the application in a background capable iOS environment my problem is the following: The application does not detect an entry which has been deleted in the system address book until the application is closed completely (iOS 4 killing it with the red X in springboard). If the application is simply sent to the background, a contact is deleted from the system address book, and then the app brought to the foreground again the function ABRecordRef() for the deleted entry id does not return NULL as would be expected for an entry which no longer exists, it returns the entry as if it had never been removed from the system address book database.
As above - if the application is completely terminated and restarted then ABRecordRef() for the deleted id works as expected - it returns NULL. Is there any way to detect this deleted entry without waiting for the app to be killed and restarted?
Yes. Register for changes to the address book with ABAddressBookRegisterExternalChangeCallback, e.g.
// Your method called when the user has granted access to their address book data.
- (void)accessGrantedForAddressBook
{
ABAddressBookRegisterExternalChangeCallback(sharedAddressBook, MyAddressBookExternalChangeCallback, (__bridge void *)(self));
}
Where MyAddressBookExternalChangeCallback is your callback function.
I belive that you need to create a new ABAddressBook every time you refresh data from the address book. The reason is this:
You might have added, updated and removed records in your copy of the address book but not yet called Save or Revert. Hence, if the system (e.g. iOS build in contact book app) changes things in the address book, you cannot get them automatically.
Try either running Revert or to recreate the addressbook and it should work.