MKMapView User Location Timeout using Parse.com - ios

So I am using parse to get an update of the users location and data from that query like so:
-(void) getUserLocationAndData:(BOOL) showProgress
{
[PFGeoPoint geoPointForCurrentLocationInBackground:^(PFGeoPoint *geoPoint, NSError *error)
{
if (!error)
{
NSLog(#"Got current location - Zooming Map!");
CLLocationCoordinate2D zoomLocation;
zoomLocation.latitude = geoPoint.latitude;
zoomLocation.longitude = geoPoint.longitude;
[self getNearByData:geoPoint];
}
else
{
[[[UIAlertView alloc] initWithTitle:#"Unable to get Current Location" message:#"This app needs your current location in order to locate near by data. Please check your internet connection and make sure you enabled access to the location information." delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil] show];
}
}];
}
When the app first launches the user location shows up, the data is pulled and everything works. If you wait about 15 seconds and don't move the user location icon turns gray (see image) and any updates result in a failure. If I reload the view it works again but the same thing happens.
I am not using a CLLocationManager because I do not need to constantly pull the data near the user. Just every so often or on demand by the user.
I have also noticed if I wait long enough the user location goes Blue again and all seems to work. What is causing this timeout? Can I set this timeout, or do I need to just use a CLLocationManager to have any control of this? Is Parse just timing out with an internal CLLocationManager or something?
Thanks for the help!

So evidently when you use the Parse function: geoPointForCurrentLocationInBackground Parse will take over with it's own internal CLLocationManager. After it does it's initial thing it stops updating the location (or possibly only updates the user location if the user moves enough.. again just a guess here.. and if that feature is supported on the device).
The solution is to create your own CLLocationManager in your view controller and instead of calling
geoPointForCurrentLocationInBackground
You simply store the updated user location and call:
PFGeoPoint *usersGeoPoint = [PFGeoPoint geoPointWithLocation:usersLastKnownLocation];
And now we can use that to query near by data. We are now responsible for starting and stopping the updating of the user location. I strongly recommend using this post to setup your own LocationManager and be sure to scroll down to the updated answers for more up to date info.
How can I get current location from user in iOS
After reading about the geoPointWithLocation in the Parse docs I made the conclusions above. Please read it for yourself (I know it's really hard to find and just one line...)

Related

Ios location service background issue

In my iOS app I turned on location services in background and also set on always. I also set up region monitoring for every 500 meters so if iOS kills my app in background then it will wake up using region monitoring.
But I found one major issue in updating location. I disabled location services of iOS and re enable it, my app is still in background but it's location icon showing disable and other app which is not even in background shows enable. Please see attached screenshot.
If any one knows about this then please guide me.
I think you have wrong concept, the arrow unfilled means that the app is using geofences, if you scroll to the bottom in that list you can read the legend as you can se in this screenshot
Hope this helps you
In app delegate.m,
- (BOOL)application:(UIApplication )application didFinishLaunchingWithOptions:(NSDictionary )launchOptions
{
[self setUpLocationManager];
return YES;
}
#pragma mark - CLLocationManager Delegate
-(void)setUpLocationManager
{
LocationManager=[[CLLocationManager alloc]init];
LocationManager.delegate=self;
LocationManager.desiredAccuracy=kCLLocationAccuracyBest;
[LocationManager requestWhenInUseAuthorization];
[LocationManager startUpdatingLocation];
}
-(void)locationManager:(CLLocationManager )manager didUpdateLocations:(NSArray<CLLocation > *)locations
{
CLGeocoder *GioCoder=[[CLGeocoder alloc]init];
[GioCoder reverseGeocodeLocation:LocationManager.location completionHandler:^(NSArray<CLPlacemark > Nullable placemarks, NSError * Nullable error)
{
if (error !=nil)
{
}
else
{
CLPlacemark *place=[placemarks lastObject];
NSLog(#"%#",place);
NSString *Path=[[NSBundle mainBundle]pathForResource:#"countries" ofType:#"json"];
NSError *Error;
NSData *JsonData=[[NSData alloc]initWithContentsOfFile:Path options:NSDataReadingMappedAlways error:&Error];
NSMutableArray *DataArray=[[NSMutableArray alloc]init];
if (Error==nil)
{
NSError *err;
DataArray=[NSJSONSerialization JSONObjectWithData:JsonData options:NSJSONReadingMutableContainers error:&err];
}
for (int i=0; i<DataArray.count;i++)
{
if ([[[DataArray objectAtIndex:i]valueForKey:#"code"] isEqualToString:place.ISOcountryCode])
{
// Insert your code here
[LocationManager stopUpdatingLocation];
break;
}
}
}
}];
}
-(void)locationManager:(CLLocationManager )manager didFailWithError:(NSError )error
{
if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined || [CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied || [CLLocationManager authorizationStatus] == kCLAuthorizationStatusRestricted)
{
NSLog(#"Location not enabled in your device");
}
else
{
}
}
Just as Reiner said in his answer, your understanding is wrong. If you have an empty purple arrow it means your geofence is set correctly.
If another app is staying filled purple. It could be because of multiple reasons:
It has pausesLocationUpdatesAutomatically set to false and is always tracking.
It has pausesLocationUpdatesAutomatically set to true but hasn't yet paused long enough at a location to trigger a pause which would technically turn that filled purple into either gray or empty purple (which means the app is continuing tracking using significant location changes or regionMonitoring or visits monitoring)
AFAIK if you region is setup for 500meters then it's better to use SignificantLocation Changes. Because you can use cellTower information. For regionMonitoring app turns it's location Tracking on and off every few minutes to make sure it's been exited the app or not.
VERY IMPORTANT NOTE
Apple documents on this are very confusing. It took me a whole week to figure it out!
Region Monitoring or significant location Tracking or visit Monitoring will only launch app in the background if and only if your app was terminated. If your app is launched again then right from the DidFinishLaunching you can startLocationUpdates.
If your app was suspended or for some reason still in the background you would only get the didExitRegion callback. And AFIAK from the callback you're not able to startUpdateLocations. This is a very vague text from WWDC.
Finally, you must start your location updates while in the
foreground. If you don’t start your updates in the foreground, you
won’t get this special behavior. So what happens if you do start your
updates in the background? Well, first off, your app will probably
need AlwaysAuthorization since your app won’t be considered in use at
that time. Furthermore, Core Location won’t take any action to
ensure your app continues to run. So if you have background runtime
for some reason, and you decide to start a location session, you
might get some updates, but you might also get suspended before you receive all the information you had hoped to receive.
WWDC 2016: Session 716
I don't know why Apple is saying might...it could be something that varies based on battery/cell coverage/ usage patterns / iOS version. It's obviously something they don't want us developers know how. They want to force us to go for other ways.
The only way for you to get your app into terminated state is either if you user manually kills the app or you wait for a very very long time ie wait for any or all other background tasks to finish + then once app is suspended wait for it to become terminated and then exit the region to launch app and be able to start tracking Locations

iOS reverseGeocodeLocation error [duplicate]

I have a search bar in my application that the user can type an address into, and it will come up with the geocoded result. The result updates as the user types, according to the following code:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
...
if (self.geocoder.geocoding) [self.geocoder cancelGeocode];
[self.geocoder geocodeAddressString:searchText completionHandler:^(NSArray *placemarks, NSError *error) {
if (error != nil) {
NSLog(#"ERROR during geocode: %#", error.description);
return;
}
//update the view
}];
}
This works for the first few characters the user enters into the search field. However, after the user types more characters repeatedly, the geocoder starts giving the following error (which I know means that there was a problem with the network):
ERROR during geocode: Error Domain=kCLErrorDomain Code=2 "The operation couldn’t be completed. (kCLErrorDomain error 2.)"
The geocoder does not work again until the entire ViewController is reloaded. Why could this be happening, and what can I do to resolve it?
I believe the reason is the following:
Apple's geocoder does not answer every request in the same way. Instead, the first requests from a certain device are answered quickly, but if the device has sent say 100 requests or more, the answers arrive slower and slower or requests are not answered at all, which might cause your error.
When you reload the view controller, this simply takes time, and the geocoding server is more willing to answer again.
Essentially, you cannot do anything about it, since the geocoder sever wants to protect itself from being overloaded by requests from a single device. You simply had to limit the number of requests that you send there.
BTW: The docs say "you should not send more than one geocoding request per minute".
Note that this same error is returned when the device is offline.
I had this problem while picking location for messenger application.
My solution was to introduce delay of 3 seconds, after user stop panning map, before geocoder call. To ensure that user want exactly that location.
I was using 3 delegate methods
func mapView(_ mapView: GMSMapView, willMove gesture: Bool)
func mapView(_ mapView: GMSMapView, didChange position: GMSCameraPosition)
func mapView(_ mapView: GMSMapView, idleAt position: GMSCameraPosition)
And I was calling the reverse geolocation API in each of the methods. I got triggered the error message.
The error mainly because you are requesting the reverse geolocation API multiple times and more frequently.
How?
-> When you are about to start dragging, the first delegate method fires
-> When I was dragging the view, the camera is being changed, so the second delegate method is being fired and requesting geolocation API
-> When the camera is idle, the third delegate method is fired.
For my case, I had to show the location data in a label, like Uber set on the map, and I analyzed I need the data actually when the camera position is idle. Like I want to get the data of 10KM distance place, do I need the intermediate 9KM data?
so I removed the geolocation call from the first and second delegate method and kept only in the 3rd one. I was setting Loading.. in the label when the delegate methods got fired.
Fetching data in the background thread, because I don't want to hang up the main thread for this.
Also kept a 1-second delay before fetching, just for keeping a separation between the 2 API calls.

MKDirections calculateETAWithCompletionHandler: in background state

I have an app which monitors significant location changes.
Upon receiving a new calculation I want to calculate the duration from the current location to a specified location.
To calculate the duration I use calculateETAWithCompletionHandler: from the MKDirections class.
Everything works as expected as long as the app is in the foreground.
When I send the app to the background, it is correctly receives location updates in the background and everything works until I call calculateETAWithCompletionHandler:, which will never return results.
MKDirectionsHandler, the completion handler of calculateETAWithCompletionHandler:. is never called when being in the background.
As soon as the app is coming into the foreground again, all the waiting completion handlers are receiving results.
MKMapItem* origin = [MKMapItem mapItemForCurrentLocation];
MKMapItem* destination = [[MKMapItem alloc] initWithPlacemark:destinationPlacemark];
MKDirectionsRequest* request = [MKDirectionsRequest new];
[request setSource:origin];
[request setDestination:destination];
[request setTransportType:MKDirectionsTransportTypeAutomobile];
MKDirections* directions = [[MKDirections alloc] initWithRequest:request];
[directions calculateETAWithCompletionHandler:^(MKETAResponse *response, NSError *error) {
completion(response.expectedTravelTime, error);
}];
Is calling calculateETAWithCompletionHandler: in the background not allowed?
Is there any way to resolve this issue?
I believe the way you are making use of MKMapItem is the problem, you need to run this on the main thread. So I don't think it will work for what you need. When collecting the location in the background you should use CoreLocation instead.
The documentation around MKDirection is not very specific on this issue, the most relevant section I could find was:
An MKDirections object provides you with route-based directions data from Apple servers. You can use instances of this class to get travel-time information or driving or walking directions based on the data in an MKDirectionsRequest object that you provide. The directions object passes your request to the Apple servers and returns the requested information to a block that you provide.
Since you are trying to calculate travel-time, it would appear that calculateETAWithCompletionHandler: tries to perform a network request to the apple servers. With the application being in a background state, the request is put on hold until the application enters foreground again.
Unfortunately I don't think there is an easy way around this. You could try and use a "guesstimation" approach where, before the application enters a background state it calculates the ETA for a user, and then while it is in the background it increases or decreases the ETA proportionally to the direct distance between your current location and the destination. Depending on how precise you want your results to be this broad estimation could be enough to satisfy your requirements.

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.

iOS: how to execute an high number of reverseGeocoding request?

i have to execute a high number of reverseGeocodeLocation request,
i use this method for doing that:
for (Photo *photo in arrayWhitPicture) {
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[queue addOperationWithBlock:^{
[geocoder reverseGeocodeLocation:[[CLLocation alloc] initWithLatitude:[photo.latitude doubleValue] longitude:[photo.longitude doubleValue]] completionHandler:^(NSArray *placemarks, NSError *error) {
if (error){
NSLog(#"Geocode failed with error: %#", error);
return;
}
CLPlacemark *myPlacemark = [placemarks objectAtIndex:0];
NSString *city = myPlacemark.locality;
NSLog(#"My country code: %#", city);
}];
}];
}
this code actual work, but the problem is that some of these request (like half) get this error:
Error Domain=kCLErrorDomain Code=2
after a few research i think this happen because i do a lot of request in short amount of time in fact the apple documentations say:
Send at most one geocoding request for any one user action.
When you want to update the user’s current location automatically
(such as when the user is moving), issue new geocoding requests only
when the user has moved a significant distance and after a reasonable
amount of time has passed. For example, in a typical situation, you
should not send more than one geocoding request per minute.
so my question is: my error is really caused by the fact that i do a lot of request, and in that case what can i do to resolve this problem, do you know other system apart to use the reverseGeocoding?
I have had exactly this problem, and my solution was to throttle down the number of reverse geocode queries that I performed (i.e. temporarily suspend the queue if already processed a whole bunch). This worked so well that I did not have to implement plan B, which was to switch to a different service.
See for example this question for discussion of alternate services. Google has a similar limit of 2500 requests per API key and 24 hour period. There is also Bing.
Apple most definitely limits how many geocoding requests you can issue at a time. Other users are reporting that the limit is around 50, though that could change at any time. The recommendation seems to be to do the geocoding in batches and to issue only one batch at a time, starting each batch only after the previous one completes.
Apple limits to perform number of Reverse GeoCode requests your application can make at a time. Sometimes I have seen this limited to ONE.
The solution is to implement your own Reverse Geocoder Queue (you can implement it as a separate class), in which you can add all of your requests. This queue need to execute one request at a time and after first is done, execute next. You can add callback blocks to notify you once the reverse geocoding is done for each request.
Example API in the Reverse Geocoder queue class can be like:
- (void) reverseGeocodeLocation: (CLLocation *) location completion: (CLGeocodeCompletionHandler) completionHandler
{
// Create some queue (NSMutableArray) in the class
// Create some ReverseGeoLocationObject with location and completionHandler as members
// Add ReverseGeoLocationObject to queue
// Check is queue is not already processing. If NO then process next request. You have have API named processNextRequest which you can call here (put code you posted in this API for single request)
}
Also call processNextRequest when CLGeocoder returns.

Resources