CLLocation Variable not sending KVO - NSKeyValueObservingOptionNew - ios

I am working on a simple application that gathers a users current data in a location model. That is working and I can log the locations. However, no viewControllers are seeing the updated variable. I am using KVO in the viewControllers to fire but it seems that CLLocation does not send using 'NSKeyValueObservingOptionNew'. It will fire if I use the 'NSKeyValueObservingOptionInitial' but this fires before the CLLocationManager has had an opportunity to set the instance.
Does anyone know why CLLocation is not sending the "...New" value? Like I said, it is working only when it is set to the "...Initial" state.
Thoughts?
View Controller Code:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if ([keyPath isEqual:#"locationUpdated"]) {
CLLocation *locationVariable = [[CLLocation alloc]init];
locationVariable = self.location.currentLocation;
NSLog(#"the location vaiable: %#", locationVariable);
self.theMap.centerCoordinate = self.location.currentLocation.coordinate;
}
}
Location Model Code:
- (void)startLocationManger {
if ([self locationManager] == nil) self.locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
_locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[_locationManager startUpdatingLocation];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
CLLocation *locationFix = [locations lastObject];
_currentLocation = locationFix;
NSLog(#"Current Location: %#", _currentLocation);
}

The main reason the KVO notification is not firing from the Location Model code is probably this line in the locationManager:didUpdateLocations delegate method:
_currentLocation = locationFix;
The code is assigning a value directly to the backing variable of the property you want to observe.
Automatic KVO notifications only fire if you use the synthesized property setter:
self.currentLocation = locationFix; // <-- assign using self.xxx
A bit unrelated but in the observeValueForKeyPath method:
I'm not sure why you're checking if ([keyPath isEqual:#"locationUpdated"]). Are you sure that's the keyPath you want to look for?
In this code:
CLLocation *locationVariable = [[CLLocation alloc]init];
locationVariable = self.location.currentLocation;
the alloc+init is unnecessary since the variable is then immediately assigned a new value. Just do:
CLLocation *locationVariable = self.location.currentLocation;

Related

Multiple Local Notifications glitch from didEnterRegion:

I'm Currently monitoring several locations that are backed by core data.
In other words, I have set up a for loop that loops through all of the stored entities in core data and creates a monitored region for all of the entities.
The problem here is that the for loop triggers multiple local notifications when entering one of the regions. The number of notifications almost directly corresponds to the number of monitored regions. So I'm fairly confident this may be whats causing the bug, but I'm not 100 percent sure.
I've noticed that this seems to be a common issue with region monitoring, but I haven't been unable to find an example that incorporates a for loop.
How can I stop multiple notifications being triggered when didEnterRegion gets called?
The method below is called in viewDidLoad. The [DataSource sharedInstance].fetchedResultItems is an array that is populated with the fetchedObjects from a fetched request.
-(void)startMonitoringRegions{
if ([self.locationManager respondsToSelector:#selector(requestWhenInUseAuthorization)]) {
[self.locationManager requestWhenInUseAuthorization];
CLAuthorizationStatus authorizationStatus = [CLLocationManager authorizationStatus];
if (authorizationStatus == kCLAuthorizationStatusAuthorizedAlways ||
authorizationStatus == kCLAuthorizationStatusAuthorizedWhenInUse) {
self.locationManager.distanceFilter = 10;
[self.locationManager startUpdatingLocation];
for (POI *items in [DataSource sharedInstance].fetchResultItems){
NSString *poiName = items.name;
NSNumber *poiLatitude = items.yCoordinate;
NSLog(#"value: %#", poiLatitude);
NSNumber *poiLongitude = items.xCoordinate;
NSLog(#"value: %#", poiLongitude);
NSString *identifier = poiName;
CLLocationDegrees latitude = [poiLatitude floatValue];
CLLocationDegrees longitude = [poiLongitude floatValue];
CLLocationCoordinate2D centerCoordinate = CLLocationCoordinate2DMake(latitude, longitude);
self.regionRadius = 10;
self.region = [[CLCircularRegion alloc] initWithCenter:centerCoordinate radius:400 identifier:identifier];
[self.locationManager startMonitoringForRegion:self.region];
NSLog(#"region: %#", self.region);
NSLog(#"monitored regions %#", self.locationManager.monitoredRegions);
}
}
}
}
Here is the didEnterRegion method
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region{
NSLog(#"entered region!!");
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
if (localNotification) {
localNotification.fireDate = nil;
localNotification.alertBody = [NSString stringWithFormat:#"You are near %#", self.region.identifier];
localNotification.fireDate = [NSDate dateWithTimeIntervalSinceNow:10];
localNotification.timeZone = [NSTimeZone defaultTimeZone];
}
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
// [[UIApplication sharedApplication]presentLocalNotificationNow:localNotification];
}
Regions are act as a shared resources. When you enter any region a call will be forwarded to all of the location manager. I think somewhere somehow you are creating multiple location manager objects. That is actually causing the multiple calling of didEnterRegion. The number of time didEnterRegion is called depending upon the number of LocationManager you registered. You should write the code in AppDelegate, in this method
(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//Place your code here
}
Just a troubleshooting tip. You can use Obj-C equivalent of the following to see what regions are currently being monitored by the app. Perhaps reviewing the identifiers will shed some light on the problem.
for region in locationManager.monitoredRegions {
debugPrint(region.identifier)
}
And for a clean start you can delete all regions with this:
for region in locationManager.monitoredRegions {
locationManager.stopMonitoringForRegion(region)
}

Core Location can't get data in viewDidLoad

I wrote a simple iOS application that retrieves location information and then uses the location to request Yahoo Weather.
The problem is that even when I call the Core Location in the viewDidLoad, it won't give me the result immediately.
So why can't I get the location information?
How can I get the location information in viewDidLoad?
The pseudocode currently is something like:
- (void)viewDidLoad
{
[super viewDidLoad];
self.locManager = [[CLLocationManager alloc] init];
self.locManager.delegate = self;
self.locManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locManager.distanceFilter = 100;
[self.locManager startUpdatingLocation];
//won't get the current location right now, so the output will be null
NSLog(#"Current Location Longitude: %#", self.longitudeString);
NSLog(#"Current Location Latitude: %#", self.latitudeString);
}
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
CLLocation *newLocation = [locations lastObject];
self.longitudeString = [NSString stringWithFormat:#"%.8f", currentLocation.coordinate.longitude];
self.latitudeString = [NSString stringWithFormat:#"%.8f", currentLocation.coordinate.latitude];
}
Location updates are not provided as instantly as you are expecting, you need to wait few seconds (2-3 or may be more) to get precise location update. If you want to have location data in viewDidLoad then you should init your location manager (and call startUpdatingLocation) before invoking the ViewController (since then it is not guaranteed that you will have location-data in viewDidLoad).

IOS how to get live latitude and longitude

I want the current latitude and longitude of my device. I can print latitude and longitude when my program locate the device for the first time but I can't refresh it.
-(void) locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
int lastindex = [locations count]-1;
CLLocation * currentLocation = [locations objectAtIndex:lastindex];
float latitude =currentLocation.coordinate.latitude;
float longitude =currentLocation.coordinate.longitude;
NSLog(#"LAT:%f LONG:%f",latitude,longitude);
[self latitudeLabel].text = [[NSString alloc]initWithFormat:#"latitude : %#",[[NSNumber numberWithFloat:latitude] stringValue]];
[self longitudeLabel].text = [[NSString alloc]initWithFormat:#"longitude : %#",[[NSNumber numberWithFloat:longitude] stringValue]];
}
This is what I tried but latitude/longitude are never updated. I tried it with the function didUpdateHeading but that didn't work either.
How can i do this. Any one can help me to solve this issue.
Thanks you
This method is triggered by startUpdatingLocation which I assume you're using since it did get called at least once. The method gets called each time the location data changes. Is your location actually changing enough to trigger it? Also, check the distanceFilter property on your CLLocationManager and make sure it is low enough.
Make sure you add the following to your header:
#interface YourLocationObject : SomeObject <CLLocationManagerDelegate>
.. and when you create your CLLocationManager object, you set yourself as the delegate:
CLLocationManager *locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
//... other customization here ...//
[locationManager startUpdatingLocations];

Core location gives old location whenever comes from background

I have a CoreLocation manager in VC, when user pressed "get direction" button, I initalize location manager, and app opens google map direction with current location and some pre defined destination location.
Here is my problem, if app is not in background state, current location nearly always true, bu if app calling from background in same VC and user pressed again "get direction" button , current location generally shows old locations. In short, I'm troubling with multitasking and timestamp of retrieved locations did not solved my problem.
IBAction:
if ( self.locationManager ) {
[_locationManager release];
self.locationManager = nil;
}
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locationManager.distanceFilter = kCLDistanceFilterNone;
self.locationTimer = [NSTimer scheduledTimerWithTimeInterval:LOCATION_TIMER target:self selector:#selector(stopUpdatingLocationTimer) userInfo:nil repeats:NO];
HUD = [MBProgressHUD showHUDAddedTo:self.navigationController.view animated:YES];
[self.locationManager startUpdatingLocation];
Core Location Delegate:
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
NSTimeInterval locationAge = -[newLocation.timestamp timeIntervalSinceNow];
NSLog(#"%f",locationAge);
if (locationAge > 3.0)
return;
if (newLocation.horizontalAccuracy < 0)
return;
if ( self.currentLocation == nil || self.currentLocation.horizontalAccuracy > newLocation.horizontalAccuracy ) {
self.currentLocation = newLocation;
if (self.currentLocation.horizontalAccuracy <= self.locationManager.desiredAccuracy) {
[self stopUpdatingLocations:YES];
}
}
}
In your example locationAge is a negative representation of the number of seconds since the timestamp of newLocation. This means that locationAge will never be greater than 3 and you're effectively letting every update through the sieve.
Set locationAge like this:
NSTimeInterval locationAge = [newLocation.timestamp timeIntervalSinceNow];
For those who encounter same problem,
Also, some tutorials related with core location on web, lead me this problem.
Of course, I keep CLLocation ivar in my VC, whenever CLLocation ivar
sets and google maps called, my app goes to background.
Then, my app calls from background by user, and start updating locations,
and old CLLocation ivar is not nil and probably best horizantal accuracy then
newly comes. Therefore;
if ( self.currentLocation == nil || self.currentLocation.horizontalAccuracy > newLocation.horizontalAccuracy )
this line cause problems, and old location in CLLocation ivar
never replaced.
So I changed viewDidDisappear like this and I assigned nil value to CLLocation variable and works perfectly.
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
[self.locationManager stopUpdatingLocation]; // CLLocationManager
self.locationManager = nil;
[self.locationTimer invalidate]; // NSTimer
self.locationTimer = nil;
self.currentLocation = nil; // CLLocation
}
p.s : thank you Mark Adams

Application crashes when requesting CLLocation property from appDelegate

I have an application which creates a class instance that contains (amongst other things) some location data.
In the app delegate I set up location services and start grabbing the location data;
//Delegate method to receive location information from locationManager
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
latestLocation = newLocation;//Make latest location the same as NewLocation
NSLog(#"Location is: %#", latestLocation);
}
I declare latest location as a property so that I can get at the CLLocation instance from another class.
My captures class, when called grabs the CLLocation when its init method is called;
//Designated initialiser
-(id) initWithVideoPath:(NSString *) vPath
userNotes:(NSString *) uNotes
retentionState:(NSString *) rState
{
//Call the super classes designated initializer
[super init];
//Get a pointer to the application delegate so we can access the location props
Rolling_VideoAppDelegate *appDelegate = (Rolling_VideoAppDelegate*)[UIApplication sharedApplication].delegate;
//If superclass failed to init
if (!self)
return nil;
//Give the variables some initial values
[self setVideoPath:vPath];
[self setUserNotes:uNotes];
[self setRetentionState:rState];
dateCreated = [[NSDate alloc] init];
mp = [[MapPoint alloc]initWithCoordinate:[[appDelegate latestLocation]coordinate]];//get the location from the coords from appDelegate
return self;
[dateCreated release];
}
However the app crashes when the mapPoint init is called. The problem is I'm not getting the CLLocation information in properly.
I'm still unsure why the original solution does not work, so if anyone has any insights please do enlighten.
I have however worked out a slightly inelegant work around using NSUserDefaults
latestLocation = newLocation;//Make latest location the same as NewLocation
//Use NSUser Defaults to save the CLLocation instance
NSUserDefaults *location = [NSUserDefaults standardUserDefaults];
[location setDouble:latestLocation.coordinate.latitude forKey:#"lat"];
[location setDouble:latestLocation.coordinate.longitude forKey:#"longd"];
I needed to break out the lat and long as NSUserDefaults will not store CLLocation objects (NSCoding compatability), the reconstruct them in the captures class;
NSUserDefaults *location = [NSUserDefaults standardUserDefaults];//Get a handle to the user defaults
CLLocationDegrees lat = [location doubleForKey:#"lat"];
CLLocationDegrees longd = [location doubleForKey:#"longd"];
CLLocation *currentLocation = [[CLLocation alloc] initWithLatitude:lat longitude:longd];
mp = [[MapPoint alloc]initWithCoordinate:[currentLocation coordinate]];//get the location from the coords from appDelegate

Resources