Reputation: 135
I am writing an iOS app for scanning barcodes at participating retail locations, where that location would donate to charity on the customer's behalf after the customer scans a QRcode printed on the receipt.
I would like to send a local notification to a user if they are in a participating location for 60 seconds or longer, reminding them to scan any receipts they might get from purchases they made there.
My issue is that I would like to delay a call for 60 seconds when a user enters a region - if after those 60 seconds they are still in that region fire off the local notification - however, sometimes that call to sendLocalNotification within stillInRegion doesn't fire until the app returns to the foreground. I believe this has to do with the thread sometimes ending before the delay is up, but I am not sure. I have tried about every approach I could find on stackoverflow and elsewhere (blocks, nstimers, etc.) but to no avail. Any ideas about how to better approach this problem?
- (void) sendLocalNotification:(NSString *)regionId {
NSLog(@"we entered %@ and we're currently in %@", regionId, self.currentRegionId);
if ([regionId isEqualToString:self.currentRegionId]) {// if we're still in the region, send a local notification
UILocalNotification *localNotif = [[UILocalNotification alloc] init];
if (localNotif == nil) return;
NSDate *fireTime = [[NSDate date] addTimeInterval:2]; // adds 2 secs
localNotif.fireDate = fireTime;
localNotif.alertBody = [NSString stringWithFormat:@"Did you just visit %@? If so, don't forget to scan your receipt!", regionId];
localNotif.applicationIconBadgeNumber = [UIApplication sharedApplication].applicationIconBadgeNumber+1;
[[UIApplication sharedApplication] scheduleLocalNotification:localNotif];
[localNotif release];
}
}
- (void) stillInRegion:(CLRegion *)region {
NSLog(@"did enter region: %@", region.identifier);
[self performSelector:@selector(sendLocalNotification:) withObject:region.identifier afterDelay:60];
}
- (void) locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
if (self.didLaunchForRegionUpdate) {
NSString *path = [DGGeofencingHelper applicationDocumentsDirectory];
NSString *finalPath = [path stringByAppendingPathComponent:@"notifications.dg"];
NSMutableArray *updates = [NSMutableArray arrayWithContentsOfFile:finalPath];
if (!updates) {
updates = [NSMutableArray array];
}
NSMutableDictionary *update = [NSMutableDictionary dictionary];
[update setObject:region.identifier forKey:@"fid"];
[update setObject:[NSNumber numberWithDouble:[[NSDate date] timeIntervalSince1970]] forKey:@"timestamp"];
[update setObject:@"enter" forKey:@"status"];
[updates addObject:update];
[updates writeToFile:finalPath atomically:YES];
} else {
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setObject:@"enter" forKey:@"status"];
[dict setObject:region.identifier forKey:@"fid"];
NSString *jsStatement = [NSString stringWithFormat:@"DGGeofencing.regionMonitorUpdate(%@);", [dict JSONString]];
[self.webView stringByEvaluatingJavaScriptFromString:jsStatement];
}
self.currentRegionId = region.identifier;
self.cRegionEnterTime =[NSDate date];
[self stillInRegion:region];
}
Upvotes: 3
Views: 1627
Reputation: 135
inside my didEnterRegion method I used to following code to achieve the desired result:
UIApplication* app = [UIApplication sharedApplication];
bgTaskIdentifier = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTaskIdentifier];
bgTaskIdentifier = UIBackgroundTaskInvalid;
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSTimer* t = [NSTimer scheduledTimerWithTimeInterval:180 target:self selector:@selector(stillInRegion:) userInfo:region.identifier repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:t forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
});
Pretty close to what @lucaslt89 had but a slight variation on it.
Upvotes: 1
Reputation: 2451
Are the locationManager:didEnterRegion being called in foreground and then your application goes to background? I'm not very clear about when you are calling those methods, but you could try creating a background task as follows:
Before your call to [self stillInRegion:region]; add the following code:
bgTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{}];
It should call your stillInRegion method, and continues even if you go to the background, so the delay should continue counting on!. Finally, you should end your background task. To do this, add the following line at the end of your sendLocalNotification method, after the if block:
[[UIApplication sharedApplication] endBackgroundTask:bgTaskIdentifier];
Just let us know if that was helpful! and excuse me for my poor english!
Have a good day!
Upvotes: 1