All of my code is in AppDelegate.m:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc]initWithFrame:[[UIScreen mainScreen]bounds]];
_locationMgr = [[CLLocationManager alloc] init];
[_locationMgr setDelegate:self];
if([_locationMgr respondsToSelector:#selector(setAllowsBackgroundLocationUpdates:)])
[_locationMgr setAllowsBackgroundLocationUpdates:YES];
CLAuthorizationStatus authorizationStatus= [CLLocationManager authorizationStatus];
if([launchOptions valueForKey:UIApplicationLaunchOptionsLocationKey] != nil) {
NSLog(#"relaunching because of significant location change - restarting SLC");
[_locationMgr startMonitoringSignificantLocationChanges];
}
else
{
if (authorizationStatus == kCLAuthorizationStatusAuthorizedAlways) {
NSLog(#"launching with authorization to always use location - starting SLC");
[_locationMgr startMonitoringSignificantLocationChanges];
}
else
{
NSLog(#"launching with no authorization to always use location - requesting authorization");
if([_locationMgr respondsToSelector:#selector(requestAlwaysAuthorization)])
[_locationMgr requestAlwaysAuthorization];
}
}
if([userdefaults objectForKey:#"pfuser"] == nil) {
NSLog(#"in delegate signup");
SignUpController *signup = [[SignUpController alloc] init];
[self.window setRootViewController:signup];
}
else {
ViewController *map = [[ViewController alloc] init];
[self.window setRootViewController:map];
}
[self.window makeKeyAndVisible];
return YES;
}
- (void)startSignificantChangeUpdates
{
deviceNotFoundAlertController = [UIAlertController alertControllerWithTitle:#"START" message:#"startSignificantChangeUpdates called" preferredStyle:UIAlertControllerStyleAlert];
[deviceNotFoundAlertController addAction:deviceNotFoundAlert];
// Create the location manager if this object does not
// already have one.
if (nil == _locationMgr) {
_locationMgr = [[CLLocationManager alloc] init];
_locationMgr.delegate = self;
}
[CLLocationManager significantLocationChangeMonitoringAvailable];
[_locationMgr startMonitoringSignificantLocationChanges];
}
-(void)locationManger:(CLLocationManager *)manager didFailWithError:(NSError *)error {
NSLog(#"didFailWithError: %#", error);
deviceNotFoundAlertController = [UIAlertController alertControllerWithTitle:#"LOCATION FAIL" message:#"didFailWithError" preferredStyle:UIAlertControllerStyleAlert];
[deviceNotFoundAlertController addAction:deviceNotFoundAlert];
}
// Delegate method from the CLLocationManagerDelegate protocol.
- (void)_locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations {
deviceNotFoundAlertController = [UIAlertController alertControllerWithTitle:#"LOCATION UPDATE" message:#"didUpdateLocations called" preferredStyle:UIAlertControllerStyleAlert];
[deviceNotFoundAlertController addAction:deviceNotFoundAlert];
// If it's a relatively recent event, turn off updates to save power.
CLLocation* location = [locations lastObject];
NSDate* eventDate = location.timestamp;
NSTimeInterval howRecent = [eventDate timeIntervalSinceNow];
if (fabs(howRecent) < 15.0) {
// If the event is recent, do something with it.
NSLog(#"latitude %+.6f, longitude %+.6f\n",
location.coordinate.latitude,
location.coordinate.longitude);
}
}
None of the alerts happen, it seems like the delegate methods aren't being called.
UPDATE
Now I have:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc]initWithFrame:[[UIScreen mainScreen]bounds]];
deviceNotFoundAlert = [UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleDefault handler:nil];
...
}
// Delegate method from the CLLocationManagerDelegate protocol.
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations {
deviceNotFoundAlertController = [UIAlertController alertControllerWithTitle:#"LOCATION UPDATE" message:#"didUpdateLocations called" preferredStyle:UIAlertControllerStyleAlert];
[deviceNotFoundAlertController addAction:deviceNotFoundAlert];
// If it's a relatively recent event, turn off updates to save power.
CLLocation* location = [locations lastObject];
NSDate* eventDate = location.timestamp;
NSTimeInterval howRecent = [eventDate timeIntervalSinceNow];
if (fabs(howRecent) < 15.0) {
// If the event is recent, do something with it.
NSLog(#"latitude %+.6f, longitude %+.6f\n",
location.coordinate.latitude,
location.coordinate.longitude);
}
}
When I test the app, I open it at my house, and then close it, so that when I leave my house it should send an alert (or 3) at some point, but I am not getting alerts from any of the delegate methods (where I placed alerts).
I just had an idea, maybe I have to display the alerts from the main UIViewController, not the AppDelegate?
This may be why I am not seeing the alerts: How do I add a UIAlertController in app delegate (obj-c)
UPDATE
This is how I am doing the alerts now:
deviceNotFoundAlertController = [UIAlertController alertControllerWithTitle:#"START" message:#"startSignificantChangeUpdates called" preferredStyle:UIAlertControllerStyleAlert];
[deviceNotFoundAlertController addAction:deviceNotFoundAlert];
alertWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
alertWindow.rootViewController = [[UIViewController alloc] init];
alertWindow.windowLevel = UIWindowLevelAlert + 1;
[alertWindow makeKeyAndVisible];
[alertWindow.rootViewController presentViewController:deviceNotFoundAlertController animated:YES completion:nil];
UPDATE
The alerts did not seem to be the issue, the alert in startSignificantChangeUpdates never appears. Should it appear once I am 500m from my initial location?
UPDATE
Can anyone help me understand this?
The methods of your delegate object are called from the thread in which you started the corresponding location services. That thread must itself have an active run loop, like the one found in your application’s main thread.
UPDATE
I think I figured out what the above quote is saying...and I have this now - I will test tomorrow.
...
if([launchOptions valueForKey:UIApplicationLaunchOptionsLocationKey] != nil) {
NSLog(#"relaunching because of significant location change - restarting SLC");
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[_locationMgr startMonitoringSignificantLocationChanges];
});
}
else
{
if (authorizationStatus == kCLAuthorizationStatusAuthorizedAlways) {
NSLog(#"launching with authorization to always use location - starting SLC");
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[_locationMgr startMonitoringSignificantLocationChanges];
});
}
else
{
NSLog(#"launching with no authorization to always use location - requesting authorization");
if([_locationMgr respondsToSelector:#selector(requestAlwaysAuthorization)])
[_locationMgr requestAlwaysAuthorization];
}
}
...
I think that code is starting the location services on its own thread. One thing I noticed already, is that when I exit the app, the location in the top right goes away. I just updated to iOS 10. In iOS 9 the location arrow in the top right would stay there, but it would only be a black outline when the app was not running. This could just be something they changed with iOS 10, or now because I updated to 10, something else isn't working now. Or that is what happens when the location services are run on their own thread. From here: iOS start Background Thread
UPDATE
Maybe I am not using the thread correctly, but as I said, now when I close the app, location services quits. When I was doing it without the thread the location service arrow would stay in the top right, as an outline.
UPDATE
I read that the service should be started on the main thread - so now I have:
CLAuthorizationStatus authorizationStatus= [CLLocationManager authorizationStatus];
NSLog(#"launching with no authorization to always use location - requesting authorization");
if([_locationMgr respondsToSelector:#selector(requestAlwaysAuthorization)]) {
[_locationMgr requestAlwaysAuthorization];
}
if([launchOptions valueForKey:UIApplicationLaunchOptionsLocationKey] != nil) {
NSLog(#"relaunching because of significant location change - restarting SLC");
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[_locationMgr startMonitoringSignificantLocationChanges];
});
}
else if (authorizationStatus == kCLAuthorizationStatusAuthorizedAlways) {
NSLog(#"launching with authorization to always use location - starting SLC");
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[_locationMgr startMonitoringSignificantLocationChanges];
});
}
else {
//
}
The arrow in the right doesn't show up when the app is closed, is this something new to iOS 10 where they don't show it anymore?
UPDATE
I accidentally deleted: _locationMgr = [[CLLocationManager alloc] init]; I put in and now the arrow is always there, going to test today.
UPDATE
I tested it, still no alerts.
It is a problem with your delegate method please replace below one
- (void)_locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations {
}
with
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations {
}
Hope it will help you.
I took my computer with me in my car, and watched the console, and I saw that the significant location changes are happening now because I get location updates every 500m. The alerts are the only thing not working, but they are irrelevant to the program - they were just there to see if it was working. It is working with this code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc]initWithFrame:[[UIScreen mainScreen]bounds]];
...
_locationMgr = [[CLLocationManager alloc] init];
[_locationMgr setDelegate:self];
if([_locationMgr respondsToSelector:#selector(setAllowsBackgroundLocationUpdates:)])
[_locationMgr setAllowsBackgroundLocationUpdates:YES];
CLAuthorizationStatus authorizationStatus= [CLLocationManager authorizationStatus];
NSLog(#"launching with no authorization to always use location - requesting authorization");
if([_locationMgr respondsToSelector:#selector(requestAlwaysAuthorization)]) {
[_locationMgr requestAlwaysAuthorization];
}
if([launchOptions valueForKey:UIApplicationLaunchOptionsLocationKey] != nil) {
NSLog(#"relaunching because of significant location change - restarting SLC");
[_locationMgr startMonitoringSignificantLocationChanges];
}
else if (authorizationStatus == kCLAuthorizationStatusAuthorizedAlways) {
NSLog(#"launching with authorization to always use location - starting SLC");
[_locationMgr startMonitoringSignificantLocationChanges];
}
else {
//
}
...
[self.window makeKeyAndVisible];
return YES;
}
- (void)startSignificantChangeUpdates
{
// Create the location manager if this object does not
// already have one.
if (nil == _locationMgr) {
_locationMgr = [[CLLocationManager alloc] init];
_locationMgr.delegate = self;
}
[CLLocationManager significantLocationChangeMonitoringAvailable];
[_locationMgr startMonitoringSignificantLocationChanges];
deviceNotFoundAlertController = [UIAlertController alertControllerWithTitle:#"START" message:#"startSignificantChangeUpdates called" preferredStyle:UIAlertControllerStyleAlert];
}
-(void)locationManger:(CLLocationManager *)manager didFailWithError:(NSError *)error {
NSLog(#"didFailWithError: %#", error);
}
// Delegate method from the CLLocationManagerDelegate protocol.
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations {
// If it's a relatively recent event, turn off updates to save power.
CLLocation* location = [locations lastObject];
NSDate* eventDate = location.timestamp;
NSTimeInterval howRecent = [eventDate timeIntervalSinceNow];
if (fabs(howRecent) < 15.0) {
// If the event is recent, do something with it.
NSLog(#"latitude %+.6f, longitude %+.6f\n",
location.coordinate.latitude,
location.coordinate.longitude);
}
}
You have written write code, Just add below delegate method in your code. But startMonitoringSignificantLocationChanges for updating location take 10 to 20 min. and also trigger if location channel change.
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
}
[_locationMgr startMonitoringSignificantLocationChanges];
The significant-change location service delivers updates only when there has been a significant change in the device’s location, such as 500 meters or more.
So your delegate method will call each time once when your device moved more than 500 meter.
Make sure your app have background location permission.
if your app is in background or foreground then it will call delegate method
otherwise app will launch with location option in AppDelegate file where you have to create Location manager object and start location again to get new location.
https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/LocationAwarenessPG/CoreLocation/CoreLocation.html
Related
I am looking for a solution to access/stop location services while app is in the background. My app takes continuous location when it's sent to background (It has access to continuous location) . It's necessary for the app functionality.
So I would like to know few things:
How long my app can take continuous location while it's still in the background? (before OS kills the background process or something like that)
If I want to add a timer say after 60 minutes app will stop taking the location, what would be the correct approach?
Background location updation can be done using following code:
In Appdelegate class:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if ([launchOptions objectForKey:UIApplicationLaunchOptionsLocationKey]) {
// This "afterResume" flag is just to show that he receiving location updates
// are actually from the key "UIApplicationLaunchOptionsLocationKey"
self.shareModel.afterResume = YES;
[self.shareModel startMonitoringLocation];
}
return YES;
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
[self.shareModel stopContinuosLocationUpdate];
[self.shareModel restartMonitoringLocation];
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
//Remove the "afterResume" Flag after the app is active again.
self.shareModel.afterResume = NO;
[self.shareModel startContinuosLocationUpdate];
}
In Location update class, say LocationManager.m:
#import <CoreLocation/CoreLocation.h>
#property (nonatomic) CLLocationManager * anotherLocationManager;
- (void)startContinuosLocationUpdate
{
CLAuthorizationStatus status = [CLLocationManager authorizationStatus];
if (status == kCLAuthorizationStatusDenied)
{
NSLog(#"Location services are disabled in settings.");
}
else
{
// for iOS 8
if ([self.anotherLocationManager respondsToSelector:#selector(requestAlwaysAuthorization)])
{
[self.anotherLocationManager requestAlwaysAuthorization];
}
// for iOS 9
if ([self.anotherLocationManager respondsToSelector:#selector(setAllowsBackgroundLocationUpdates:)])
{
[self.anotherLocationManager setAllowsBackgroundLocationUpdates:YES];
}
[self.anotherLocationManager startUpdatingLocation];
}
}
- (void)stopContinuosLocationUpdate
{
[self.anotherLocationManager stopUpdatingLocation];
}
- (void)startMonitoringLocation
{
if (_anotherLocationManager)
[_anotherLocationManager stopMonitoringSignificantLocationChanges];
self.anotherLocationManager = [[CLLocationManager alloc]init];
_anotherLocationManager.delegate = self;
_anotherLocationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
_anotherLocationManager.activityType = CLActivityTypeOtherNavigation;
if(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"9.0")) {
[_anotherLocationManager setAllowsBackgroundLocationUpdates:YES];
}
else if(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"8.0")) {
[_anotherLocationManager requestAlwaysAuthorization];
}
[_anotherLocationManager startMonitoringSignificantLocationChanges];
}
- (void)restartMonitoringLocation
{
[_anotherLocationManager stopMonitoringSignificantLocationChanges];
if(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"9.0")) {
[_anotherLocationManager setAllowsBackgroundLocationUpdates:YES];
}
else if(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"8.0")) {
[_anotherLocationManager requestAlwaysAuthorization];
}
[_anotherLocationManager startMonitoringSignificantLocationChanges];
}
#pragma mark - CLLocationManager Delegate
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
if(_dictLocation && [_dictLocation isKindOfClass:[NSDictionary class]])
{
float latitudeValue = [[RVCommon validateDataForNumber:_dictLocation[#"lat"]] floatValue];
float longitudeValue = [[RVCommon validateDataForNumber:_dictLocation[#"lng"]] floatValue];
CLLocation *facilityLocation = [[CLLocation alloc] initWithLatitude:latitudeValue longitude:longitudeValue];
CLLocation *mostRecentLocation = locations.lastObject;
CLLocationDistance distanceInMeters = [mostRecentLocation distanceFromLocation:facilityLocation];
if (distanceInMeters <= 500.0)
{
//Here I am informing the server when user is within 500mts of the coordinate.
}
}
NSLog(#"locationManager didUpdateLocations: %#",locations);
}
I know there is a lot of questions related to that,But I cant able to link with my Issue. In my app,I am fetching the Nearby Restaurant,Initially,If user clicks on 'Nearby' button I fetch the lat,long,placemark details,Using this code below.
-(void)setUpUserLocation
{
BOOL locationAllowed = [CLLocationManager locationServicesEnabled];
if (locationAllowed==NO)
{
UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:#"No authorization"
message:#"Please, enable access to your location"
delegate:self cancelButtonTitle:#"Cancel"
otherButtonTitles:#"Open Settings", nil];
[alertView show];
}
else
{
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
if ([locationManager respondsToSelector:#selector(requestWhenInUseAuthorization)]) {
[locationManager requestWhenInUseAuthorization];
}
}
[locationManager startUpdatingLocation];
}
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
CLLocation *location=[locations lastObject];
CLGeocoder *geocoder=[[CLGeocoder alloc]init];
[geocoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error) {
CLPlacemark *placemark = placemarks[0];
NSDictionary *addressDictionary = [placemark addressDictionary];
}];
[self stopSignificantChangesUpdates];
}
- (void)stopSignificantChangesUpdates
{
[locationManager stopUpdatingLocation];
locationManager = nil;
}
My Question is, If the user location changes, how do I give the user an alert like Your location changed, you need to update it then only you can get a Nearby Restaurant Location popup should come once the user location changes otherwise don't want to call the didUpdateLocation everytime. I called startMonitoringSignificantLocationChanges then it did not called the didUpdateLocation in first time itself.Any Help on this.
check after some time or check after user's movement(whatever you want),if condition is true then call service...
I think it help you;
Step 1
(void)viewDidLoad {
[super viewDidLoad];
locationManager = [[CLLocationManager alloc] init];
//set the amount of metres travelled before location update is made
[locationManager setDistanceFilter:100];
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingLocation];
[locationManager requestAlwaysAuthorization];
// call the timer with 5 minutes cap using 5 * 60 = 300
[NSTimer scheduledTimerWithTimeInterval:300.0f target:self selector:#selector(sendlocation1) userInfo:nil repeats:YES];
Step 2
Every 100 Meter change Device This Method is called :
-(void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
NSLog(#"%f",newLocation.coordinate.latitude);
NSLog(#"%f",newLocation.coordinate.longitude);
CLLocationDistance meters = [newLocation distanceFromLocation:oldLocation];
if(meters >=100)
{
// call webservice for location is updated
[self sendlocation1];
}else
{
// call normal method
}
}
I built a simple ios app with IBeacon, when the app is in foreground or background it works ok but after rebooting my phone, my app stops getting CoreLocation delegate callbacks. This is my AppDelegate.m code.
#import "AppDelegate.h"
#import <CoreLocation/CoreLocation.h>
#import "ViewController.h"
#interface AppDelegate ()
#end
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if ([UIApplication instancesRespondToSelector:#selector(registerUserNotificationSettings:)]) {
[[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeSound
categories:nil]];
}
if(launchOptions != nil){
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"alerta"
message:[launchOptions[UIApplicationLaunchOptionsLocalNotificationKey] description]
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
NSUUID *beaconUUID = [[NSUUID alloc] initWithUUIDString:#"B39ED98FF-2900-441A-802F-9C398FC199D2"];
NSString *regionIdentifier = #"iBeacons region 1";
CLBeaconRegion *beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID: beaconUUID identifier: regionIdentifier ];
beaconRegion.notifyEntryStateOnDisplay = YES;
self.locationManager = [[CLLocationManager alloc]init];
if([self.locationManager respondsToSelector:#selector(requestAlwaysAuthorization)]){
[self.locationManager requestAlwaysAuthorization];
}
self.locationManager.delegate = self;
//self.locationManager.pausesLocationUpdatesAutomatically = NO;
[self.locationManager startMonitoringForRegion:beaconRegion];
[self.locationManager startRangingBeaconsInRegion:beaconRegion];
[self.locationManager startUpdatingLocation];
return YES;
}
-(void)sendLocalNotificationWithMessage:(NSString*)message {
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody = message;
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
}
- (void) locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region{
ViewController *viewController = (ViewController*) self.window.rootViewController;
viewController.beacons = beacons;
[viewController.tableView reloadData];
NSString *message = #"";
if(beacons.count > 0){
CLBeacon *nearestBeacon = beacons.firstObject;
if(nearestBeacon.proximity == self.lastProximity || nearestBeacon.proximity == CLProximityUnknown){
return;
}
self.lastProximity = nearestBeacon.proximity;
switch (nearestBeacon.proximity) {
case CLProximityFar:
message = #"CLProximityFar";
break;
case CLProximityNear:
message= #"CLProximityNear";
break;
case CLProximityImmediate:
message= #"CLProximityImmediate";
break;
case CLProximityUnknown:
return;
}
}else {
message = #"No BEACONS";
}
NSLog(#"%#", message);
[self sendLocalNotificationWithMessage:message];
}
- (void) locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region{
[manager startRangingBeaconsInRegion:(CLBeaconRegion*) region];
[self.locationManager startUpdatingLocation];
NSLog(#"INSIDE REGION");
[self sendLocalNotificationWithMessage:#"INSIDE REGION"];
}
- (void) locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region{
[manager stopRangingBeaconsInRegion:(CLBeaconRegion*) region];
[self.locationManager stopUpdatingLocation];
NSLog(#"OUTSIDE REGION");
[self sendLocalNotificationWithMessage:#"OUTSIDE REGION"];
}
- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region{
if (state == CLRegionStateInside) {
//Start Ranging
[manager startRangingBeaconsInRegion:(CLBeaconRegion*) region];
[self.locationManager startUpdatingLocation];
NSLog(#"INSIDE REGION");
[self sendLocalNotificationWithMessage:#"INSIDE REGION"];
}
else{
//Stop Ranging
[manager stopRangingBeaconsInRegion:(CLBeaconRegion*) region];
[self.locationManager stopUpdatingLocation];
NSLog(#"OUTSIDE REGION");
[self sendLocalNotificationWithMessage:#"OUTSIDE REGION"];
}
}
-(void)application:(UIApplication *)application didReceiveLocalNotification:(NSDictionary *)userInfo {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"ALERT"
message:#"didReceiveLocalNotification"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
- (void)applicationWillResignActive:(UIApplication *)application {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
- (void)applicationWillTerminate:(UIApplication *)application {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
#end
I cant find what is the issue, i need your help!
The code looks OK. When testing this, I would recommend you do the following:
Launch your app
Turn on your beacon
Verify you get a didEnterRegion callback.
Turn off your beacon
Verify you get a didExitRegion callback.
Reboot your phone
Wait at least 2 minutes (it takes a bit of time before CoreLocation fully starts up after reboot.)
Turn on your beacon
Wait a few minutes to see if you get a didEnterRegion callback.
In my app I want to fetch and upload location on server in regular intervals. for foreground it works fine but for background it has issue as described below. One thing to mention here I use different class for foreground and separate code for background.
To fetch location in background I do the following. It does upload location for about 24 - 40 hours but get stopped after 24-40 hours. I did not get "willTerminate" event. I have also written code to capture global execption so that I could log SigKill if user kill my app but it too does not get logged reason could be we can't catch SigKill.
Now I am stuck don't know what to do to keep my app alive in background to upload location in regular intervals.
//////////////////////////////// didfinish lauch ///////////////////////////////
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
return YES;
}
////////// **did enter background**
- (void) applicationDidEnterBackground:(UIApplication *)application
{
self.bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:self.bgTask];
self.bgTask = UIBackgroundTaskInvalid;
}];
timer = [[NSTimer scheduledTimerWithTimeInterval:8*60
target:self
selector:#selector(changeAccuracy)
userInfo:nil
repeats:YES] retain];
[self.locationManager startUpdatingLocation];
}
- (void) changeAccuracy
{
self.bCallbackStarted = NO;
[self.locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
[self.locationManager setDistanceFilter:100];
}
///////// **did enter fore ground**
- (void)applicationWillEnterForeground:(UIApplication *)application
{
[self.locationManager stopUpdatingLocation];
}
////////// **did update location**
-(void)locationManager:(CLLocationManager *)lm didUpdateLocations:(NSArray *)locations
{
if (self.bCallbackStarted == YES) {
NSString* logStr = [NSString stringWithFormat:#"URLFilteringAppDelegate::didUpdateLocations not uploading location because self.bCallbackStarted == YES"];
LOGI(logStr)
return;
}
self.bCallbackStarted = YES;
[[AppUtility sharedInstance] setLocationStatus:YES];
CLLocation *location = [locations lastObject];
if(location.coordinate.latitude == 0 && location.coordinate.longitude == 0)
{
[UserSettings sharedInstance].Latitude = [NSString stringWithFormat:#"%#", #"Unavailable"];
[UserSettings sharedInstance].Longitude = [NSString stringWithFormat:#"%#", #"Unavailable"];
}
else
{
[UserSettings sharedInstance].Latitude = [NSString stringWithFormat:#"%.8f", location.coordinate.latitude];
[UserSettings sharedInstance].Longitude = [NSString stringWithFormat:#"%.8f", location.coordinate.longitude];
}
[[LocationManager sharedInstance] sendLocation];
/// this is set to save battery
[lm setDesiredAccuracy:kCLLocationAccuracyThreeKilometers];
[lm setDistanceFilter:99999];
}
I have this code to wait for some time to get high accurate data:
-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation{
if((newLocation.horizontalAccuracy < 150 && newLocation.horizontalAccuracy > 0 /* valid */ && abs(howRecent) < 15.0 /*If it's a relatively recent event less than 15 seconds */))
}
the issue in other part of my application I need to wait for this method to come back, so i used:
[NSThread sleepForTimeInterval:5]; // wait 5 seconds
Is this a good practice, or there is a better way to wait for another task execution.
There is already a sample project provided by Apple that demonstrates this exact scenario.
The project is called LocateMe and can be found here.
You can find the relevant methods in GetLocationViewController.m.
In a few words, the logic behind it is to discard any cached values and accumulate location readings until the horizontalAccuracy meets the desiredAccuracy requirements. I hope that this will get you on the right track.
#import "LocationTracker.h"
#define LATITUDE #"latitude"
#define LONGITUDE #"longitude"
#define ACCURACY #"theAccuracy"
#define IS_OS_8_OR_LATER ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
#implementation LocationTracker
AppDelegate *objApp;
+ (CLLocationManager *)sharedLocationManager {
static CLLocationManager *_locationManager;
#synchronized(self) {
if (_locationManager == nil) {
_locationManager = [[CLLocationManager alloc] init];
_locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
}
}
return _locationManager;
}
- (id)init {
if (self==[super init]) {
//Get the share model and also initialize myLocationArray
self.shareModel = [LocationShareModel sharedModel];
self.shareModel.myLocationArray = [[NSMutableArray alloc]init];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(applicationEnterBackground) name:UIApplicationDidEnterBackgroundNotification object:nil];
objApp = (AppDelegate *)[[UIApplication sharedApplication] delegate];
}
return self;
}
-(void)applicationEnterBackground{
CLLocationManager *locationManager = [LocationTracker sharedLocationManager];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
locationManager.distanceFilter = kCLDistanceFilterNone;
if(IS_OS_8_OR_LATER) {
[locationManager requestAlwaysAuthorization];
}
[locationManager startUpdatingLocation];
//Use the BackgroundTaskManager to manage all the background Task
self.shareModel.bgTask = [BackgroundTaskManager sharedBackgroundTaskManager];
[self.shareModel.bgTask beginNewBackgroundTask];
}
- (void) restartLocationUpdates
{
NSLog(#"restartLocationUpdates");
if (self.shareModel.timer) {
[self.shareModel.timer invalidate];
self.shareModel.timer = nil;
}
CLLocationManager *locationManager = [LocationTracker sharedLocationManager];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
locationManager.distanceFilter = kCLDistanceFilterNone;
if(IS_OS_8_OR_LATER) {
[locationManager requestAlwaysAuthorization];
}
[locationManager startUpdatingLocation];
}
- (void)startLocationTracking {
NSLog(#"startLocationTracking");
if ([CLLocationManager locationServicesEnabled] == NO) {
NSLog(#"locationServicesEnabled false");
UIAlertView *servicesDisabledAlert = [[UIAlertView alloc] initWithTitle:#"Location Services Disabled" message:#"You currently have all location services for this device disabled" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[servicesDisabledAlert show];
} else {
CLAuthorizationStatus authorizationStatus= [CLLocationManager authorizationStatus];
if(authorizationStatus == kCLAuthorizationStatusDenied || authorizationStatus == kCLAuthorizationStatusRestricted){
NSLog(#"authorizationStatus failed");
} else {
NSLog(#"authorizationStatus authorized");
CLLocationManager *locationManager = [LocationTracker sharedLocationManager];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
locationManager.distanceFilter = kCLDistanceFilterNone;
if(IS_OS_8_OR_LATER) {
[locationManager requestAlwaysAuthorization];
}
[locationManager startUpdatingLocation];
}
}
}
- (void)stopLocationTracking {
NSLog(#"stopLocationTracking");
if (self.shareModel.timer) {
[self.shareModel.timer invalidate];
self.shareModel.timer = nil;
}
CLLocationManager *locationManager = [LocationTracker sharedLocationManager];
[locationManager stopUpdatingLocation];
}
#pragma mark - CLLocationManagerDelegate Methods
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
NSLog(#"locationManager didUpdateLocations");
for(int i=0;i<locations.count;i++){
CLLocation * newLocation = [locations objectAtIndex:i];
CLLocationCoordinate2D theLocation = newLocation.coordinate;
CLLocationAccuracy theAccuracy = newLocation.horizontalAccuracy;
NSTimeInterval locationAge = -[newLocation.timestamp timeIntervalSinceNow];
if (locationAge > 30.0)
{
continue;
}
//Select only valid location and also location with good accuracy
if(newLocation!=nil&&theAccuracy>0
&&theAccuracy<2000
&&(!(theLocation.latitude==0.0&&theLocation.longitude==0.0))){
self.myLastLocation = theLocation;
self.myLastLocationAccuracy= theAccuracy;
NSMutableDictionary * dict = [[NSMutableDictionary alloc]init];
[dict setObject:[NSNumber numberWithFloat:theLocation.latitude] forKey:#"latitude"];
[dict setObject:[NSNumber numberWithFloat:theLocation.longitude] forKey:#"longitude"];
[dict setObject:[NSNumber numberWithFloat:theAccuracy] forKey:#"theAccuracy"];
//Add the vallid location with good accuracy into an array
//Every 1 minute, I will select the best location based on accuracy and send to server
[self.shareModel.myLocationArray addObject:dict];
}
}
//If the timer still valid, return it (Will not run the code below)
if (self.shareModel.timer) {
return;
}
self.shareModel.bgTask = [BackgroundTaskManager sharedBackgroundTaskManager];
[self.shareModel.bgTask beginNewBackgroundTask];
//Restart the locationMaanger after 1 minute
self.shareModel.timer = [NSTimer scheduledTimerWithTimeInterval:30 target:self
selector:#selector(restartLocationUpdates)
userInfo:nil
repeats:NO];
//Will only stop the locationManager after 10 seconds, so that we can get some accurate locations
//The location manager will only operate for 10 seconds to save battery
if (self.shareModel.delay10Seconds) {
[self.shareModel.delay10Seconds invalidate];
self.shareModel.delay10Seconds = nil;
}
self.shareModel.delay10Seconds = [NSTimer scheduledTimerWithTimeInterval:10 target:self
selector:#selector(stopLocationDelayBy10Seconds)
userInfo:nil
repeats:NO];
}
//Stop the locationManager
-(void)stopLocationDelayBy10Seconds{
CLLocationManager *locationManager = [LocationTracker sharedLocationManager];
[locationManager stopUpdatingLocation];
NSLog(#"locationManager stop Updating after 10 seconds");
}
- (void)locationManager: (CLLocationManager *)manager didFailWithError: (NSError *)error
{
// NSLog(#"locationManager error:%#",error);
switch([error code])
{
case kCLErrorNetwork: // general, network-related error
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Network Error" message:#"Please check your network connection." delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil];
[alert show];
}
break;
case kCLErrorDenied:{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Enable Location Service" message:#"You have to enable the Location Service to use this App. To enable, please go to Settings->Privacy->Location Services" delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil];
[alert show];
}
break;
default:
{
}
break;
}
}
//Send the location to Server
- (void)updateLocationToServer {
NSLog(#"updateLocationToServer");
// Find the best location from the array based on accuracy
NSMutableDictionary * myBestLocation = [[NSMutableDictionary alloc]init];
for(int i=0;i<self.shareModel.myLocationArray.count;i++){
NSMutableDictionary * currentLocation = [self.shareModel.myLocationArray objectAtIndex:i];
if(i==0)
myBestLocation = currentLocation;
else{
if([[currentLocation objectForKey:ACCURACY]floatValue]<=[[myBestLocation objectForKey:ACCURACY]floatValue]){
myBestLocation = currentLocation;
}
}
}
NSLog(#"My Best location:%#",myBestLocation);
//If the array is 0, get the last location
//Sometimes due to network issue or unknown reason, you could not get the location during that period, the best you can do is sending the last known location to the server
if(self.shareModel.myLocationArray.count==0)
{
NSLog(#"Unable to get location, use the last known location");
self.myLocation=self.myLastLocation;
self.myLocationAccuracy=self.myLastLocationAccuracy;
}else{
CLLocationCoordinate2D theBestLocation;
theBestLocation.latitude =[[myBestLocation objectForKey:LATITUDE]floatValue];
theBestLocation.longitude =[[myBestLocation objectForKey:LONGITUDE]floatValue];
self.myLocation=theBestLocation;
self.myLocationAccuracy =[[myBestLocation objectForKey:ACCURACY]floatValue];
}
NSLog(#"Send to Server: Latitude(%f) Longitude(%f) Accuracy(%f)",self.myLocation.latitude, self.myLocation.longitude,self.myLocationAccuracy);
//TODO: Your code to send the self.myLocation and self.myLocationAccuracy to your server
//After sending the location to the server successful, remember to clear the current array with the following code. It is to make sure that you clear up old location in the array and add the new locations from locationManager
[self.shareModel.myLocationArray removeAllObjects];
self.shareModel.myLocationArray = nil;
self.shareModel.myLocationArray = [[NSMutableArray alloc]init];
}
#end