using UILocalNotification with core location - ios

I have this code which send user notifications every time the distance between an event that occur in a local JSON file and his current location is < 100 meter asking him whether he is at that event or not , when he presses on yes then that event will be marked as attended. the thing is I tried to do that by using some code i found online but I'm not sure if it is the right way to do it, anyway i tested it on my iPhone and what happened is when i arrived to an event location it kept sending unstoppable notifications and when i try to press yes or no nothing actually happen it keeps sending these notifications. Can anyone plz explain for me what is going wrong, I'm not very familiar with Xcode and objective-C language. The code i used is shown below.
in AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// load Core Data
NSManagedObjectContext *context = [self managedObjectContext];
if (!context) {
NSLog(#"No NSManagedObjectContext generated");
}
NSLog(#"DelegateApp Managed Object Context = %#", context);
[[DataManager sharedInstance] setManagedObjectContext:context];
[[DataManager sharedInstance] initDataBase];
return YES;
UILocalNotification *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (notification) {
[self showAlarm:notification.alertBody];
NSLog(#"AppDelegate didFinishLaunchingWithOptions");
application.applicationIconBadgeNumber = 0;
}
[self.window makeKeyAndVisible];
return YES;
}
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
[self showAlarm:notification.alertBody];
application.applicationIconBadgeNumber = 0;
NSLog(#"AppDelegate didReceiveLocalNotification %#", notification.userInfo);
}
- (void)showAlarm:(NSString *)text {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"SPOT IT"
message:text delegate:self
cancelButtonTitle:#"YES"
otherButtonTitles:#"NO",nil];
[alertView show];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
NSString *title = [alertView buttonTitleAtIndex:buttonIndex];
if([title isEqualToString:#"NO"])
{
NSLog(#"Button 2 was selected.");
}
else if([title isEqualToString:#"YES"])
{
NSLog(#"Button 1 was selected.");
// attended
[_eachEvent setHasATTENDED:[NSNumber numberWithBool:TRUE]];
// save
NSError *error = nil;
if (![_managedObjectContext save:&error])
{
NSLog(#"Error in saving");
}
}
}
in my DataManager class:
- (void) locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation {
//NSLog(#"MV_EventsDataManager new location: latitude %+.6f, longitude %+.6f\n", newLocation.coordinate.latitude, newLocation.coordinate.longitude);
for (Event *musicevent in [self loadTodaysEvents]) {
// distance
CLLocationDegrees lat = [musicevent.lat doubleValue];
CLLocationDegrees lon = [musicevent.longi doubleValue];
CLLocation *evLocation = [[CLLocation alloc] initWithLatitude:lat longitude:lon];
double distance = [evLocation distanceFromLocation:newLocation];
//NSLog(#"\t Calculated KM %# to %#", [NSString stringWithFormat:#"%.1f",(distance/1000.0)], musicevent.title);
// CLOSE !
if (distance <= 100) {
[[UIApplication sharedApplication] cancelAllLocalNotifications];
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
localNotification.alertBody = #"Are u there!";
localNotification.soundName = UILocalNotificationDefaultSoundName;
localNotification.applicationIconBadgeNumber = 1; // increment
// NSDictionary *infoDict = [NSDictionary dictionaryWithObjectsAndKeys:#"Object 1", #"Key 1", #"Object 2", #"Key 2", nil];
// localNotification.userInfo = infoDict;
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
}
}
}

Depending on how you setup the location manager, the delegate method locationManager:didUpdateToLocation:fromLocation: will typically be called once per second with location updates. So your code is posting local notifications over and over. You need to keep track of when you've posted a notification so you can avoid posting duplicates.

Related

Google place picker interrupts the background location updates in iPhone

I'm developing a location-based mobile application. My app updates the server with user's current location even when the app is killed/terminated. My app was working fine before integrating google place picker API. After installing google place picker API for ios, my app updates the location for about 5 to 10 minutes only and then it stops updating location.
//My code :
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Initialize Location Manager
sLocationManager;
//the system automatically relaunches the application into the background if a new event arrives.
// The options dictionary passed to the application:didFinishLaunchingWithOptions: method of your application delegate contains the key UIApplicationLaunchOptionsLocationKey to indicate that your application was launched because of a location event
if (launchOptions[UIApplicationLaunchOptionsLocationKey]) {
NSLog(#"---UIApplicationLaunchOptionsLocationKey");
sLocationManager.locationUpdatedInBackground = ^(NSArray<CLLocation *> *locations) {
NSLog(#"---setLocationUpdatedInBackground");
[sLocationModule saveNewLocations];
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.fireDate = [NSDate dateWithTimeIntervalSinceNow:300];
notification.alertBody = #"Your location was changed. Please run application for tracking your geoposition with best accuracy.";
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
[sLocationManager startMonitoringSignificantLocationChanges];
};
}else {
sLocationManager.locationUpdatedInForeground = ^(NSArray<CLLocation *> *locations) {
NSLog(#"---setLocationUpdatedInForeground");
[sLocationModule saveNewLocations];
};
sLocationManager.locationUpdatedInBackground = ^(NSArray<CLLocation *> *locations) {
NSLog(#"---setLocationUpdatedInBackground");
[sLocationModule saveNewLocations];
[sLocationManager startDeferredLocationUpdates];
};
// Notifications
[application cancelAllLocalNotifications];
}
return YES;
//location manager
#pragma mark - CLLocationManager Delegate
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)aLocations {
NSString *infoMessage = [NSString stringWithFormat:#"%#", aLocations];
NSLog(#"%#", infoMessage);
locations = [#[] mutableCopy];
NSLog(#"locations=%#",locations);
[aLocations enumerateObjectsUsingBlock:^(CLLocation * _Nonnull location, NSUInteger idx, BOOL * _Nonnull stop) {
// Check for location errors (speed -1.00 mps / course -1.00)
// if (location.speed >= 0 &&
// location.course >= 0) {
if (locations.count > 0) {
if ([location.timestamp timeIntervalSinceDate:[locations lastObject].timestamp] > dLocationManagerDistanceFilter ||
[location distanceFromLocation:[locations lastObject]] > dLocationUpdatesUntilTimeout) {
[locations addObject:location];
NSLog(#"location.timestamp>dLocationManagerDistanceFilter**** locations=%#",locations);
}
} else
[locations addObject:location];
NSLog(#"else***locations=%#",locations);
//save location in userdefaults
NSString *latitudeValue = [NSString stringWithFormat:#"%f", location.coordinate.latitude];
NSString *longitudeValue = [NSString stringWithFormat:#"%f",location.coordinate.longitude];
NSLog(#"new location is recieved Lat : %# Long : %#",latitudeValue,longitudeValue);
[User setUserLat:latitudeValue];
[User setUserLon:longitudeValue];
// }
}];
if ([self isInBackground]) {
NSLog(#"app is in background***");
if (self.locationUpdatedInBackground) {
backgroundTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler: ^{
[[UIApplication sharedApplication] endBackgroundTask:backgroundTaskIdentifier];
}];
self.locationUpdatedInBackground(locations);
[delegate locationManagerDidUpdateLocationInBackground];
[[NSNotificationCenter defaultCenter] postNotificationName:nLocationManagerDidUpdateLocationInBackgroundNotification
object:nil
userInfo:nil];
[self endBackgroundTask];
}
} else {
if (self.locationUpdatedInForeground) {
if([[NSUserDefaults standardUserDefaults]objectForKey:#"userIdKey"]){
self.locationUpdatedInForeground(locations);
[delegate locationManagerDidUpdateLocationInForeground];
[[NSNotificationCenter defaultCenter] postNotificationName:nLocationManagerDidUpdateLocationInForegroundNotification
object:nil
userInfo:nil];
}
}
}
}
code for adding place picker
#pragma mark - Place picker delegate methods
- (IBAction)pickPlace:(id)sender {
//dismiss the keyboard
[tfLocation resignFirstResponder];
CLLocationCoordinate2D center = CLLocationCoordinate2DMake(locationManager.location.coordinate.latitude, locationManager.location.coordinate.longitude);
CLLocationCoordinate2D northEast = CLLocationCoordinate2DMake(center.latitude + 0.001, center.longitude + 0.001);
CLLocationCoordinate2D southWest = CLLocationCoordinate2DMake(center.latitude - 0.001, center.longitude - 0.001);
GMSCoordinateBounds *viewport = [[GMSCoordinateBounds alloc] initWithCoordinate:northEast
coordinate:southWest];
GMSPlacePickerConfig *config = [[GMSPlacePickerConfig alloc] initWithViewport:viewport];
_placePicker = [[GMSPlacePicker alloc] initWithConfig:config];
[_placePicker pickPlaceWithCallback:^(GMSPlace *place, NSError *error) {
if (error != nil) {
NSLog(#"Pick Place error %#", [error localizedDescription]);
return;
}
if (place != nil) {
NSLog(#"Place name %#", place.name);
NSLog(#"Place address %#", place.formattedAddress);
NSLog(#"Place attributions %#", place.attributions.string);
// NSString *latitudeValue = [NSString stringWithFormat:#"%f", locationManager.location.coordinate.latitude];
// NSString *longitudeValue = [NSString stringWithFormat:#"%f",locationManager.location.coordinate.longitude];
tfLocation.text=place.formattedAddress;
locationPoint=[NSString stringWithFormat:#"%f,%f", place.coordinate.latitude,place.coordinate.longitude];
} else {
NSLog(#"No place selected");
}
}];
}
// Add a UIButton in Interface Builder, and connect the action to this function.
- (IBAction)getCurrentPlace:(UIButton *)sender {
[_placesClient currentPlaceWithCallback:^(GMSPlaceLikelihoodList *placeLikelihoodList, NSError *error){
if (error != nil) {
NSLog(#"Pick Place error %#", [error localizedDescription]);
return;
}
self.nameLabel.text = #"No current place";
self.addressLabel.text = #"";
if (placeLikelihoodList != nil) {
GMSPlace *place = [[[placeLikelihoodList likelihoods] firstObject] place];
if (place != nil) {
self.nameLabel.text = place.name;
self.addressLabel.text = [[place.formattedAddress componentsSeparatedByString:#", "]
componentsJoinedByString:#"\n"];
}
}
}];
}
// Present the autocomplete view controller when the button is pressed.
- (IBAction)onLaunchClicked:(id)sender {
GMSAutocompleteViewController *acController = [[GMSAutocompleteViewController alloc] init];
acController.delegate = self;
[self presentViewController:acController animated:YES completion:nil];
}
// Handle the user's selection.
- (void)viewController:(GMSAutocompleteViewController *)viewController
didAutocompleteWithPlace:(GMSPlace *)place {
[self dismissViewControllerAnimated:YES completion:nil];
// Do something with the selected place.
NSLog(#"Place name %#", place.name);
NSLog(#"Place address %#", place.formattedAddress);
NSLog(#"Place attributions %#", place.attributions.string);
}
- (void)viewController:(GMSAutocompleteViewController *)viewController
didFailAutocompleteWithError:(NSError *)error {
[self dismissViewControllerAnimated:YES completion:nil];
// TODO: handle the error.
NSLog(#"Error: %#", [error description]);
}
// User canceled the operation.
- (void)wasCancelled:(GMSAutocompleteViewController *)viewController {
[self dismissViewControllerAnimated:YES completion:nil];
}
// Turn the network activity indicator on and off again.
- (void)didRequestAutocompletePredictions:(GMSAutocompleteViewController *)viewController {
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
}
- (void)didUpdateAutocompletePredictions:(GMSAutocompleteViewController *)viewController {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
Can anyone tell, what am I doing wrong here? What is the best to use place picker without affecting background location updates or is there any other way to pick place/location just like google place picker in ios?
Please help me out or tell me the other solution.
thank you!

Region Monitoring method getting called multiple times in Geo-Fencing

I have created multiple geo-fence to monitor region entry/exit events.
I have created a location manager in AppDelegate.h file.
#interface AppDelegate : UIResponder <UIApplicationDelegate, CLLocationManagerDelegate>
#property (strong, nonatomic) UIWindow *window;
#property(nonatomic,retain)CLLocationManager *locationManager;
#property(nonatomic,retain)CLLocation *currentLocation;
+(AppDelegate *)sharedDelegate;
AppDelegate.m file
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UILocalNotification *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (notification) {
NSLog(#"AppDelegate didFinishLaunchingWithOptions");
application.applicationIconBadgeNumber = 0;
}
if ([application respondsToSelector:#selector(registerUserNotificationSettings:)])
{
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge|UIUserNotificationTypeAlert|UIUserNotificationTypeSound) categories:nil];
[application registerUserNotificationSettings:settings];
}
else // iOS 7 or earlier
{
UIRemoteNotificationType myTypes = UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound;
[application registerForRemoteNotificationTypes:myTypes];
}
if (!self.locationManager)
{
self.locationManager = [[CLLocationManager alloc] init];
}
self.locationManager.delegate = self;
//locationManager.distanceFilter = kCLDistanceFilterNone;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
self.locationManager.distanceFilter = 2.0f;
self.locationManager.activityType = CLActivityTypeAutomotiveNavigation;
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
{
[self.locationManager requestAlwaysAuthorization];
}
if ([self.locationManager respondsToSelector:#selector(allowsBackgroundLocationUpdates)])
{
self.locationManager.allowsBackgroundLocationUpdates = YES;
}
if ([self.locationManager respondsToSelector:#selector(pausesLocationUpdatesAutomatically)])
{
self.locationManager.pausesLocationUpdatesAutomatically= NO;
}
[self.locationManager stopMonitoringSignificantLocationChanges];
if ([CLLocationManager locationServicesEnabled] && [CLLocationManager authorizationStatus] != kCLAuthorizationStatusDenied)
{
[self.locationManager startUpdatingLocation];
}
// Override point for customization after application launch.
return YES;
}
-(void)locationManager:(CLLocationManager *)manager didStartMonitoringForRegion:(CLRegion *)region
{
NSLog(#"Started monitoring %# region",region.identifier);
}
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations
{
NSLog(#"%#",[locations description]);
}
-(void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
dispatch_async(dispatch_get_main_queue(), ^{
if ([[UIApplication sharedApplication] applicationState]==UIApplicationStateBackground || [[UIApplication sharedApplication] applicationState]==UIApplicationStateInactive)
{
UILocalNotification *localnotification = [[UILocalNotification alloc]init];
localnotification.fireDate=[NSDate dateWithTimeIntervalSinceNow:1];
localnotification.alertBody=#"You are enter in region.";
localnotification.timeZone=[NSTimeZone defaultTimeZone];
localnotification.repeatInterval = 0;
localnotification.hasAction=YES;
[[UIApplication sharedApplication]scheduleLocalNotification:localnotification];
}
else
{
[[[UIAlertView alloc]initWithTitle:#"message" message:#"Enter into region." delegate:self cancelButtonTitle:nil otherButtonTitles:#"Ok ", nil] show];
}
});
}
-(void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region
{
dispatch_async(dispatch_get_main_queue(), ^{
if ([[UIApplication sharedApplication] applicationState]==UIApplicationStateBackground || [[UIApplication sharedApplication] applicationState]==UIApplicationStateInactive)
{
UILocalNotification *localnotificationExit = [[UILocalNotification alloc]init];
localnotificationExit.fireDate=[NSDate dateWithTimeIntervalSinceNow:1];
localnotificationExit.alertBody=#"You are exit from region.";
NSLog(#"Exit from region.");
localnotificationExit.timeZone=[NSTimeZone defaultTimeZone];
localnotificationExit.repeatInterval = 0;
localnotificationExit.hasAction=YES;
[[UIApplication sharedApplication]scheduleLocalNotification:localnotificationExit];
}
else
{
[[[UIAlertView alloc]initWithTitle:#"message" message:#"Exit from region." delegate:self cancelButtonTitle:nil otherButtonTitles:#"Ok ", nil] show];
}
});
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
NSLog(#"didFailWithError: %#", error);
[[[UIAlertView alloc] initWithTitle:#"Error" message:#"Failed to Get Your Location" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil] show];
}
This things are to manage the region monitoring.
Now my view controller are adding the regions for monitoring.
-(void)AddRegionsInGeoFence
{
NSUserDefaults *standardDefaults = [NSUserDefaults standardUserDefaults];
//----1
CLLocationCoordinate2D centerCoordinate1 = CLLocationCoordinate2DMake(23.046518, 72.543337);
CLCircularRegion *region1 =[[CLCircularRegion alloc] initWithCenter:centerCoordinate1 radius:200 identifier:#"Location First"];
NSLog(#"%#",[region1 description]);
region1.notifyOnEntry=YES;
region1.notifyOnExit=YES;
if (![standardDefaults boolForKey:#"EnterRegion"])
{
[[AppDelegate sharedDelegate].locationManager startMonitoringForRegion:region1];
NSLog(#"Started Monitoring- %#", [region1 description]);
}
[self.mapview setShowsUserLocation:YES];
[self.mapview setUserTrackingMode:MKUserTrackingModeFollow animated:YES];
//----2
CLLocationCoordinate2D centercoordinate2=CLLocationCoordinate2DMake(23.064381, 72.531181);
CLCircularRegion *region2=[[CLCircularRegion alloc]initWithCenter:centercoordinate2 radius:200 identifier:#"Location Second"];
NSLog(#"%#",[region2 description]);
region2.notifyOnEntry=YES;
region2.notifyOnExit=YES;
if (![standardDefaults boolForKey:#"EnterRegion"])
{
[[AppDelegate sharedDelegate].locationManager startMonitoringForRegion:region2];
NSLog(#"Started Monitoring- %#", [region2 description]);
}
//----3
CLLocationCoordinate2D centercoordinate3=CLLocationCoordinate2DMake(23.083583,72.546441);
CLCircularRegion *region3=[[CLCircularRegion alloc]initWithCenter:centercoordinate3 radius:200 identifier:#"Location Third"];
NSLog(#"%#",[region3 description]);
region3.notifyOnEntry=YES;
region3.notifyOnExit=YES;
if (![standardDefaults boolForKey:#"EnterRegion"])
{
[[AppDelegate sharedDelegate].locationManager startMonitoringForRegion:region3];
NSLog(#"Started Monitoring- %#", [region3 description]);
}
//4
CLLocationCoordinate2D centercoordinate4=CLLocationCoordinate2DMake(23.122255, 72.584499);
CLCircularRegion *region4=[[CLCircularRegion alloc]initWithCenter:centercoordinate4 radius:500 identifier:#"Location Fourth"];
NSLog(#"%#",[region4 description]);
region4.notifyOnEntry=YES;
region4.notifyOnExit=YES;
if (![standardDefaults boolForKey:#"EnterRegion"])
{
[[AppDelegate sharedDelegate].locationManager startMonitoringForRegion:region4];
NSLog(#"Started Monitoring- %#", [region4 description]);
[standardDefaults setBool:YES forKey:#"EnterRegion"];
[standardDefaults synchronize];
}
}
My Problem is region monitoring methods are called multiple times even if I am not moving in side the region itself. Everything else is working fine, Accuracy buffer is around 50-80 meters that is fine for me.
-(void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
-(void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region
Also if I am turning off Wi-Fi then it's calling up these methods back to back saying exit from region and enter in to region. As far as I know GPS accuracy is depends on Wi-Fi.
Any help would be highly appreciated.
a Possible workaround in the interim while the apple bug gets fixed is to rate limit the callback; thereby not acting on all the callbacks but limiting the rate at which the callbacks can get processed.
Callback execution portions that happen before the time period expires get ignored.
Here is and example code that could assist, not tested:
The rate is limited to 2 seconds.
-(void)methodRateLimit {
#synchronized(self) {
// rate limit begin
static NSDate *lastTimeExit = nil;
if (!lastTimeExit) {
lastTimeExit = [NSDate distantPast]; // way back in time
}
NSDate *now = [NSDate date];
if ([now timeIntervalSinceDate:lastTimeExit] > 2) {
// do work here
NSLog(#"Executing");
lastTimeExit = now;
} else {
NSLog(#"Limiting");
}
}
}

After removing alert view from didReceiveLocalNotification in active state,no notification is coming in foreground

I'm implementing local notifications,there are 3 notifications to be send at different times of a day.
in my app delegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
if ([application respondsToSelector:#selector(registerUserNotificationSettings:)]) {
[application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil]];
}
}
and in DidReceiveLocalNotifications
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
NSString *checkbtn= [[NSUserDefaults standardUserDefaults]valueForKey:#"on"];
if([checkbtn isEqualToString:#"SwitchOn"])
{
UIApplicationState state = [application applicationState];
if (state == UIApplicationStateActive) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Green Actions"
message:notification.alertBody
delegate:self cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
}
// Set icon badge number to zero
application.applicationIconBadgeNumber = 0;
//}
}
if i remove this alert view,i m not getting any notification in foreground but if i use this then i m getting notification alert every second.
this is my viewcontroller code
- (void)viewDidLoad {
[super viewDidLoad];
appdelegate= (AppDelegate *) [[UIApplication sharedApplication]delegate];
[self createCustomeNavigationBar];
self.navigationController.navigationBar.translucent = NO;
//[self notificationOne];
[self.switchbutton addTarget:self action:#selector(switchToggled:) forControlEvents:UIControlEventValueChanged];
[self.switchbutton1 addTarget:self action:#selector(switchToggled1:) forControlEvents:UIControlEventValueChanged];
[self.switchbutton3 addTarget:self action:#selector(switchToggled3:) forControlEvents:UIControlEventValueChanged];
if ([[NSUserDefaults standardUserDefaults]valueForKey:#"on"])
{
UILocalNotification* n1 = [[UILocalNotification alloc] init];
n1.fireDate = [NSDate dateWithTimeIntervalSinceNow: 160];
n1.alertBody = #"one";
[[UIApplication sharedApplication] scheduleLocalNotification: n1];
}
if ([[NSUserDefaults standardUserDefaults]valueForKey:#"SwitchOneon"])
{
UILocalNotification* n2 = [[UILocalNotification alloc] init];
n2.fireDate = [NSDate dateWithTimeIntervalSinceNow: 190];
n2.alertBody = #"two";
[[UIApplication sharedApplication] scheduleLocalNotification: n2];
}
// Do any additional setup after loading the view from its nib.
}
- (void) switchToggled:(id)sender {
self.switchbutton = (UISwitch *)sender;
if ([self.switchbutton isOn]) {
NSLog(#"its on!");
[[NSUserDefaults standardUserDefaults]setObject:#"SwitchOn" forKey:#"on"];
[[NSUserDefaults standardUserDefaults]synchronize];
} else {
[[NSUserDefaults standardUserDefaults]removeObjectForKey:#"on"];
NSLog(#"its off!");
}
}
- (void) switchToggled1:(id)sender {
self.switchbutton1 = (UISwitch *)sender;
if ([self.switchbutton1 isOn]) {
NSLog(#"its on!");
[[NSUserDefaults standardUserDefaults]setObject:#"SwitchOn" forKey:#"SwitchOneon"];
[[NSUserDefaults standardUserDefaults]synchronize];
} else {
[[NSUserDefaults standardUserDefaults]removeObjectForKey:#"SwitchOneon"];
NSLog(#"its off!");
}
}
- (void) switchToggled3:(id)sender {
self.switchbutton3 = (UISwitch *)sender;
if ([self.switchbutton3 isOn]) {
NSLog(#"its on!");
[[NSUserDefaults standardUserDefaults]setObject:#"SwitchOn" forKey:#"SwitchThreeon"];
[[NSUserDefaults standardUserDefaults]synchronize];
} else {
[[NSUserDefaults standardUserDefaults]removeObjectForKey:#"SwitchThreeon"];
NSLog(#"its off!");
}
}
and also i m getting some different text in alert view which i used earlier just for testing purpose,even after changing it,i m getting the same old previous text.
I am sure that problem isn't with alert code. - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification, you're getting notification. But when your app is in foreground (or active) you should handle it manually. In your case, you're handling it with UIAlertView so it will be visible in alert, but once you removed, you're not able to see a notification when your app was in foreground (or active) state.

iOS issue in background location updates. It stops after around 24 hours

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];
}

CLBeacons in app suspended state

What I am trying to achieve is described in the following steps
1.User opens the app, navigates blah blah blah
2.User presses the home button, the app shuts itself
3.A background task for detecting CLbeacons starts. On every detection, A UILocalNotification is fired immediately, so as to make the user aware that a beacon has been discovered.
Now what happens with me
I open the app, navigate then press the home button to shut the app
The phone slowly darkens itself, i.e it goes to the slide to unlock state. Here no notification gets fired; Hell, the CLLocationManger's method -(void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region doesn't get called at all.
The phone goes to sleep. I slide open the phone to awake it; Immediately multiple `UIlocalNotifications enter.
Can anyone explain to me why is this happening?
//
// AppDelegate.m
// Beacon Proximity
//
// Created by Debanjan Chakraborty on 7/15/14.
// Copyright (c) 2014 Debanjan Chakraborty. All rights reserved.
//
#import "AppDelegate.h"
#import "CustomerRegViewController.h"
#import "IntialViewController.h"
#import "BeaconProduct.h"
#import "DBManager.h"
#import "APLDefaults.h"
#import "ScanViewController.h"
typedef NS_ENUM(NSUInteger, BeaconType)
{
Check,
Bundle
};
#interface AppDelegate ()<CLLocationManagerDelegate>
#property CLLocationManager *locationAppManager;
#property (nonatomic) NSInteger localNotificationCount;
//#property (nonatomic,strong) UILocalNotification *localNotification;
//#property (nonatomic) NSTimer *timerForNotifier;
#end
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"HasLaunchedOnce"])
{
// app already launched
}
else
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"HasLaunchedOnce"];
[[NSUserDefaults standardUserDefaults] synchronize];
[self entryDatabase];
}
[[UIApplication sharedApplication] cancelAllLocalNotifications];
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:
(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)];
UILocalNotification *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (notification)
{
NSLog(#" aache ?");
// self.localNotificationCount = 0;
UIAlertView *aw = [[UIAlertView alloc] initWithTitle:#"Case 1" message: notification.alertBody delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[aw show];
}
application.applicationIconBadgeNumber = 0;
return YES;
}
- (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
NSString *token = [[deviceToken description] stringByTrimmingCharactersInSet: [NSCharacterSet characterSetWithCharactersInString:#"<>"]];
token = [token stringByReplacingOccurrencesOfString:#" " withString:#""];
NSLog(#"content---%#", token);
}
- (void)application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError:(NSError *)err {
NSLog(#"Error %#",err.localizedDescription);
}
- (void)applicationWillResignActive:(UIApplication *)application
{
if(![[NSUserDefaults standardUserDefaults] boolForKey:#"HasLaunchedOnce"])
{
NSLog(#" Will REsign and initialise");
}
else
{
NSLog(#" Will REsign?");
}
}
-(void)applicationWillTerminate:(UIApplication *)application
{
NSLog(#" terminated");
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[[UIApplication sharedApplication] cancelAllLocalNotifications];
[self initialise];
// 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
{
application.applicationIconBadgeNumber=0;
for (NSUUID *uuid in [APLDefaults sharedDefaults].supportedProximityUUIDs)
{
CLBeaconRegion *region = [[CLBeaconRegion alloc] initWithProximityUUID:uuid identifier:[uuid UUIDString]];
[self.locationAppManager stopMonitoringForRegion:region];
}
self.locationAppManager = nil;
}
-(void)insert:(NSDate *)fire WithBeaconType:(BeaconType )beaconType ForBeacon:(CLBeacon *)beacon
{
self.localNotificationCount++;
UILocalNotification *localNotification = [UILocalNotification new];
localNotification.soundName = UILocalNotificationDefaultSoundName;
localNotification.applicationIconBadgeNumber =self.localNotificationCount;
localNotification.fireDate = [NSDate date];
localNotification.alertAction = #"Show me";
if(beaconType == Check)
{
localNotification.alertBody = [NSString stringWithFormat:#"Check out new offers"];
localNotification.alertAction = NSLocalizedString(#"Read Offer", nil);
}
else
{
localNotification.alertBody = [NSString stringWithFormat:#"You've reached offers. Grab them before they expire soon"];
localNotification.alertAction = NSLocalizedString(#"Grab Offer", nil);
}
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
NSLog(#" should Notify minor is %d and major is %d",[beacon.minor integerValue],[beacon.major integerValue]);
[[DBManager getSharedInstance] updateNotifiedTimeForBeacon:beacon];
}
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
self.localNotificationCount = 0;
[[UIApplication sharedApplication] cancelAllLocalNotifications];
}
-(UIViewController *)omegaScreen
{
UIStoryboard *strybrd = [UIStoryboard storyboardWithName:#"Storyboard" bundle:nil];
NSString *viewId=[[NSUserDefaults standardUserDefaults]objectForKey:#"custId"]?#"initial":#"custReg";
UIViewController *controller=[strybrd instantiateViewControllerWithIdentifier:viewId];
if([[NSUserDefaults standardUserDefaults] objectForKey:#"custId"])
[self initialise];
return [viewId isEqualToString:#"initial"]?(IntialViewController *)controller:(CustomerRegViewController *)controller;
}
-(void)initialise
{
self.locationAppManager = [[CLLocationManager alloc] init];
self.locationAppManager.delegate = self;
for (NSUUID *uuid in [APLDefaults sharedDefaults].supportedProximityUUIDs)
{
NSLog(#" uuid is %#",uuid.UUIDString);
CLBeaconRegion *region = [[CLBeaconRegion alloc] initWithProximityUUID:uuid identifier:[uuid UUIDString]];
region.notifyEntryStateOnDisplay=YES;
[self.locationAppManager startMonitoringForRegion:region];
}
self.localNotificationCount = -1;
}
- (void) locationManager:(CLLocationManager *)manager didStartMonitoringForRegion:(CLRegion *)region
{
NSLog(#" it did");
if([region isKindOfClass:[CLBeaconRegion class]])
{
[self.locationAppManager requestStateForRegion:(CLBeaconRegion*)region];
}
}
-(void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region
{
if (state == CLRegionStateInside)
{
//Start Ranging
[manager startRangingBeaconsInRegion:(CLBeaconRegion*)region];
}
}
- (void)locationManager:(CLLocationManager*)manager didEnterRegion:(CLRegion *)region
{
if ([region isKindOfClass:[CLBeaconRegion class]])
{
[self.locationAppManager startRangingBeaconsInRegion:(CLBeaconRegion*)region];
}
}
- (void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region
{
// if([self runningInBackground])
// {
// NSLog(#" didRangeBeacons in App Delegate");
for (CLBeacon *beacon in beacons)
{
if([[DBManager getSharedInstance]shouldNotifyFor:beacon])
{
BeaconType type;
if([beacon.major integerValue]==0)
{
type = Check;
}
else
{
type = Bundle;
}
[self insert:[NSDate date]WithBeaconType:type ForBeacon:beacon];
}
}
// }
}
-(BOOL) runningInBackground
{
UIApplicationState state = [UIApplication sharedApplication].applicationState;
BOOL result = (state == UIApplicationStateBackground);
return result;
}
-(void)resetStatus
{
NSLog(#" timer has been fired");
}
-(void)entryDatabase
{
NSMutableArray *localArray = [NSMutableArray new];
BeaconProduct *bP1 = [[BeaconProduct alloc] initWithProductSku:#"1" andId:#"1" andbTitle:#"BeaconAdTitle1" andDescription:#"Description1" andCurrency:#"Rupees" andSmallImage:#"Shampoo" andLargeImage:#"Shampoo" andPrice:[NSNumber numberWithInteger:700]];
BeaconProduct *bP2 = [[BeaconProduct alloc] initWithProductSku:#"2" andId:#"1" andbTitle:#"BeaconAdTitle2" andDescription:#"Description2" andCurrency:#"Rupees" andSmallImage:#"conditionerFree" andLargeImage:#"conditionerFree" andPrice:[NSNumber numberWithInteger:200]];
BeaconProduct *bP3 = [[BeaconProduct alloc] initWithProductSku:#"3" andId:#"2" andbTitle:#"BeaconAdTitle3" andDescription:#"Description3" andCurrency:#"Rupees" andSmallImage:#"soap" andLargeImage:#"soap" andPrice:[NSNumber numberWithFloat:20.50]];
BeaconProduct *bP4 = [[BeaconProduct alloc] initWithProductSku:#"4" andId:#"3" andbTitle:#"BeaconAdTitle4" andDescription:#"Description4" andCurrency:#"Rupees" andSmallImage:#"shirt" andLargeImage:#"shirt" andPrice:[NSNumber numberWithFloat:250]];
[localArray addObject:bP1];
[localArray addObject:bP2];
[localArray addObject:bP3];
[localArray addObject:bP4];
[[DBManager getSharedInstance] insertBeaconProduct:localArray];
}
#end
You should post your code so we can explain this definitively, but it sounds like you simply have region.notifyEntryStateOnDisplay = YES; when setting up your region. When you have that option set, you will get an extra callback to your region entry callback from CLLocationManager every time you illuminate the display.
EDIT: After seeing the code, I also noticed that the app starts monitoring when it is about to to the background, then stops it when it goes back into the foreground. I suspect this may be complicating the issue and giving you extra callbacks causing extra notifications. It doesn't look like there is any reason to keep stopping and restarting monitoring. I would suggest you simply set up monitoring of your beacon regions when the app starts up, and leave monitoring going. This combined with setting region.notifyEntryStateOnDisplay=NO should prevent the multiple notifications.
SECOND EDIT: As to point (2), you should understand that you generally CANNOT range in the background at all. Once your app leaves the foreground, you can only get callbacks to didRangeBeacons: inRegion: for about five seconds, at which time such callbacks stop until the app is woken up again. One way you can wake it up is by transitioning from being in/out of a beacon region using the monitoring APIs. However, you should understand that in the background, these transitions are not always instantaneous and can take up to 15 minutes. See here for details.

Resources