Reputation: 1455
I am trying to display a notification when my app is in the background and the device enters a region of an iBeacon and when their CLProximity is Near the notification is working, but it keeps appearing at 1 second intervals:
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region{
NSLog(@"Entered beacon region");
[self.locationManager startRangingBeaconsInRegion:self.beaconRegion];
}
- (void) locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region{
NSLog(@"Left region");
[self.locationManager stopRangingBeaconsInRegion:self.beaconRegion];
}
- (void) locationManager:(CLLocationManager *)manager didStartMonitoringForRegion:(CLRegion *)region {
[self.locationManager startRangingBeaconsInRegion:self.beaconRegion];
}
- (void) locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region {
CLBeacon *beacon = [[CLBeacon alloc] init];
beacon = [beacons lastObject];
self.uuidLabel.text = beacon.proximityUUID.UUIDString;
if(beacon.proximity == CLProximityUnknown) {
distanceLabel.text = @"Unknown Proximity";
[self.view setBackgroundColor:[UIColor grayColor]];
} else if (beacon.proximity == CLProximityImmediate) {
distanceLabel.text = @"Immediate";
[self.view setBackgroundColor:[UIColor redColor]];
} else if (beacon.proximity == CLProximityNear) {
distanceLabel.text = @"Near";
[self.view setBackgroundColor:[UIColor orangeColor]];
UILocalNotification *inRange = [[UILocalNotification alloc] init];
inRange.alertBody = [NSString stringWithFormat:@"Entered region!"];
inRange.soundName = UILocalNotificationDefaultSoundName;
[[UIApplication sharedApplication] presentLocalNotificationNow:inRange];
} else if (beacon.proximity == CLProximityFar) {
distanceLabel.text = @"Far";
[self.view setBackgroundColor:[UIColor blueColor]];
}
}
Should there be a method call after the notification is displayed to tell the app that it has been displayed and not to keep calling the didRangeBeacons method until the user goes out of range and back in again?
Upvotes: 0
Views: 696
Reputation: 64916
The multiple notifications can be solved like this:
If you only want the notification to be sent once, simply define an alreadyDisplayed
flag, which gets set after the notification is sent, then check its value before sending.
Like this:
BOOL alreadyDisplayed = NO;
...
- (void) locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region {
...
else if (beacon.proximity == CLProximityNear) {
distanceLabel.text = @"Near";
if (!alreadyDisplayed) {
[self.view setBackgroundColor:[UIColor orangeColor]];
alreadyDisplayed = YES;
UILocalNotification *inRange = [[UILocalNotification alloc] init];
inRange.alertBody = [NSString stringWithFormat:@"Entered region!"];
inRange.soundName = UILocalNotificationDefaultSoundName;
[[UIApplication sharedApplication] presentLocalNotificationNow:inRange];
}
}
...
}
But you still have a second problem:
If you want to do this in the background as your question title suggests, this isn't going to work at all. The problem is that after iOS detects that your phone entered an iBeacon region in the background, it only lets it run for five seconds before putting it to sleep. Because the range of an iBeacon is about 50 meters, the most likely case is that this five second interval will start when you are at the edge of that 50 meter range. It is very unlikely that the user will walk so fast that they enter the "Near" proximity within the 5 seconds you have before your app is put to sleep. For this reason, it is generally not possible to take specific action based upon an specific proximity when you are in the background.
That said, if you want to do this in the foreground, this will work just fine if you make changes to keep the notifications from coming every second.
Upvotes: 1
Reputation: 6990
When ranging beacons, locationManager:didRangeBeacons:inRegion
will be called every second, as you've identified. Each time, the beacons
parameter will contain an array of all visible beacons.
It's up to your app to contain logic to determine whether a new beacon is visible, or whether you've already notified the user about it. I suggest you store an array of previously discovered beacons, and each time locationManager:didRangeBeacons:inRegion
is called you compare your list with the contents of the beacons
parameter. You then should be able to tell whether any new beacons have been found.
Upvotes: 1