I have created a iphone app for beacons. In that i want to display the message when i am exit from all the beacons region.
I dont want to display the message for every beacon exit region. For example, If have 3 beacons, I want to display the message only when i am exit of all the 3 beacons. Is it possible to do that?
And also i want to get the exiting beacon major and minor values in didExitRegion
I used the following code:
-(void)locationManager:(CLLocationManager*)manager
didRangeBeacons:(NSArray*)beacons
inRegion:(CLBeaconRegion*)region
{
// Beacon found!
NSLog(#"iBeacons found");
// UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Successfully found" message:nil delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles: nil]; [alert show];
CLBeacon *foundBeacon = [beacons firstObject];
// You can retrieve the beacon data from its properties
NSString *uuid = foundBeacon.proximityUUID.UUIDString;
NSString *majorId = [NSString stringWithFormat:#"%#", foundBeacon.major];
NSString *minorId = [NSString stringWithFormat:#"%#", foundBeacon.minor];
NSLog(#"UUID: %#", uuid);
}
In the above code i can get the uuid, major, minor of the beacons. But I want to get the values of the exiting beacon in didExitRegion. Is it possible?
Thanks in advance.
Use a mutable array to keep track of the region details of the detected beacon. And update that array with detection of new beacon regions. Like the code below
In delegate method didDetermineState add
- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region
{
if(state == CLRegionStateInside)
{
[regions addObject:region]; // regions is the mutable array
}
}
And with didExitRegion method add
// Tells the delegate that the user left the specified region.
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region
{
[regions removeObject:region];
CLBeaconRegion *beaconRegion = (CLBeaconRegion *)region;
NSLog(#"\nExited region id: %#",beaconRegion.identifier);
NSLog(#"\nExited region major: %#",beaconRegion.major);
NSLog(#"\nExited region minor: %#",beaconRegion.minor);
// Add a check with regions array here to show the custom alert message
}
I dont want to display the message for every beacon exit region. For
example, If have 3 beacons, I want to display the message only when i
am exit of all the 3 beacons. Is it possible to do that?
Your requirements don't seem completely clear. If these three beacons are described by a single region you're monitoring, then it's simple: you will only get a single didExitRegion: message as you leave the range of the last beacon in the region. Otherwise, Alex's suggestion above seems reasonable: track regions you're entering and exiting, then conditionally execute some code in didExitRegion: when the count of regions you're currently in drops to zero.
And also i want to get the exiting beacon major and minor values in
didExitRegion
I'm not sure "exiting beacon" is well-defined here (do you mean, say, the last beacon in the region whose range you left, hence triggering the didExitRegion: message?), but in any case you can't get what you're asking for here. Where Alex logs major and minor, they are attributes of the region being monitored (and, in this case, being exited), not of any specific beacon.
Related
Can I detect whether a user moved to another country?
(Not using Locale.current)
The location detection should be running in background.
I'm hoping to do something like this.
Eg. A user from US leaves the country to UK. Then, when the user reach UK, i am able to detect it at the background and send notification.
You should in your Info.plist, set allowsBackgroundLocationUpdates to YES, this you can search google, and lots of answer for adapt iOS 9.
First you can use CLLocationManager to get the location:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib .
//delegate
self.locationManager.delegate = self;
//The desired location accuracy.
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
//Specifies the minimum update distance in meters.
self.locationManager.distanceFilter = kCLDistanceFilterNone;
self.locationManager.purpose = #"To provide functionality based on user's current location.";
[self.locationManager startUpdatingLocation];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation{
UIAlertView* av = [[UIAlertView alloc] initWithTitle:#"update" message:[NSString stringWithFormat:#"didUpdateToLocation: newLocation: %# old:%#",newLocation,oldLocation] delegate:nil cancelButtonTitle:#"cancel" otherButtonTitles:#"ok", nil nil];
[av show];
}
Secondly, you can use CLGeocoder to get country or city.
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
// get city name
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder reverseGeocodeLocation:newLocation completionHandler:^(NSArray *array, NSError *error)
{
if (array.count > 0)
{
CLPlacemark *placemark = [array objectAtIndex:0];
NSString *city = placemark.locality;
}
else if (error == nil && [array count] == 0)
{
NSLog(#"No results were returned.");
}
else if (error != nil)
{
NSLog(#"An error occurred = %#", error);
}
}];
}
You can give a duration to get location per duration:
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
newLocation = [locations lastObject];
double lat = newLocation.coordinate.latitude;
double lon = newLocation.coordinate.longitude;
NSLog(#"lat:%f,lon:%f",lat,lon);
if (!self.deferringUpdates) {
CLLocationDistance distance = 500;
NSTimeInterval time = 20;
[locationManager allowDeferredLocationUpdatesUntilTraveled:distance
timeout:time];
self.deferringUpdates = YES;
}
}
You can create UNNotificationRequest with exit UNLocationNotificationTrigger.
UNNotificationRequest
A UNNotificationRequest object is used to schedule a local notification and manages the content for a delivered notification. A notification request object contains a UNNotificationContent object with the contents of the notification. It also contains the UNNotificationTrigger object that specifies the conditions that trigger the delivery of the notification. For a delivered notification, you use these objects to fetch information about the notification.
UNLocationNotificationTrigger
A UNLocationNotificationTrigger object causes the delivery of a notification when the device enters or leaves a specified geographic region. Use this object to specify the region information needed to trigger the notification. Location triggers can fire once or they can fire multiple times.
Apps must request access to location services and must have when-in-use permissions to use this class. To request permission to use location services, call the requestWhenInUseAuthorization() method of CLLocationManager before scheduling any location-based triggers.
Flow
Each time user opens app, check his local country and define location trigger
let region: CLRegion = <your code defining country region>
region.notifyOnEntry = false
region.notifyOnExit = true
let trigger = UNLocationNotificationTrigger(region: region, repeats: false)
and using that trigger reschedule notification request (UNNotificationRequest).
When trigger fires (user leaves region) — app will present local notification, and if user taps on it, app starts, and if you add handler on local notification open you can notify your server about user moving away and check his new country and do what you need to do.
I am working on iBeacon transmitter and receiver. I have successfully completed the transmitter part but the other part the receiver is not recognising the transmitted signal. Can any body please help me identify where i went wrong? Is there anything more I have to add in .plist. I have tried stackoverflow answers but sorry to tell that nothing worked.
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// Initialize location manager and set ourselves as the delegate
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
// Create a NSUUID with the same UUID as the broadcasting beacon
NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:#"A77A1B68-49A7-4DBF-914C-760D07FBB87B"];
// Setup a new region with that UUID and same identifier as the broadcasting beacon
self.myBeaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid identifier:#"xx.xxxxxx.xxxxxxxx"];
// Tell location manager to start monitoring for the beacon region
[self.locationManager startMonitoringForRegion:self.myBeaconRegion];
// Check if beacon monitoring is available for this device
if (![CLLocationManager isMonitoringAvailableForClass:[CLBeaconRegion class]]) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Monitoring not available" message:nil delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles: nil];
[alert show];
return;
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)locationManager:(CLLocationManager*)manager didEnterRegion:(CLRegion *)region
{
// We entered a region, now start looking for our target beacons!
self.statusLabel.text = #"Finding beacons.";
[self.locationManager startRangingBeaconsInRegion:self.myBeaconRegion];
}
-(void)locationManager:(CLLocationManager*)manager didExitRegion:(CLRegion *)region
{
// Exited the region
self.statusLabel.text = #"None found.";
[self.locationManager stopRangingBeaconsInRegion:self.myBeaconRegion];
}
-(void)locationManager:(CLLocationManager*)manager
didRangeBeacons:(NSArray*)beacons
inRegion:(CLBeaconRegion*)region
{
// Beacon found!
self.statusLabel.text = #"Beacon found!";
CLBeacon *foundBeacon = [beacons firstObject];
// You can retrieve the beacon data from its properties
//NSString *uuid = foundBeacon.proximityUUID.UUIDString;
//NSString *major = [NSString stringWithFormat:#"%#", foundBeacon.major];
//NSString *minor = [NSString stringWithFormat:#"%#", foundBeacon.minor];
}
#end
You need to ask the permission to use the bluetooth.
Use requestAlwaysAuthorization (for background location) or requestWhenInUseAuthorization (when foreground).
You also need the NSLocationAlwaysUsageDescription or NSLocationWhenInUseUsageDescription key in Info.plist with a message to be displayed in the prompt to the user like "I need your permission to access bluetooth" or whatever.
In previous system versions, it will automatically notify the user to authorize when the location service is being used. In iOS 8, Apple has updated the authorization strategy, which requires to call the function to request user's authorization. The corresponding SDK has also provided alternative function.
1.requestAlwaysAuthorization
Firstly, the notification content is required. When calling the function, the system will push this paragraph of text to the user if he/she has not authorize the App to use the service. You may need to add the following key to Info.plist:
NSLocationAlwaysUsageDescription
Meanwhile, a written desciption should be added, without which calling the function will be invalid. Secondly, call the authorization function.
[locationManger requestAlwaysAuthorization];
Just a side thought to try and debug the issue (Although from your code everything seems to be written correctly).
First, lets see if you got some error while initialising your listener. To do that, lets implement these delegates and see if you get some error here:
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
- (void)locationManager:(CLLocationManager *)manager monitoringDidFailForRegion:(CLRegion *)region withError:(NSError *)error
Second, implement below delegate to check if location manager started monitoring your region. You can NSLog your region' UUID and identifier just to be doubly sure.
- (void)locationManager:(CLLocationManager *)manager didStartMonitoringForRegion:(CLRegion *)region
Next, if you get above call back then everything seems fine on your listener. Try couple of things now:
Is your broadcaster is really broadcasting?
If yes, is it broadcasting the same UUID your listener is expecting.
If yes, try switching off listener and broadcaster. Reboot the device and then switch on broadcaster followed by listener.
I have experienced, Location management not work instantaneously. For instance, once you detected the region entry, if you got out of the region you may not get immediate call back and then if you enter the same region again without getting exit call, you will not receive entry call. I've seen #3, working in many situations.
Also, a tip that I am not remembering where I got from :). Start ranging your beacons along with monitoring. Sometimes this gives better results.
I'm trying to access the major and minor values for the closest beacon within the didEnterRegion delegate. However, when printing the values to the console they return null
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region {
if ([region isKindOfClass:[CLBeaconRegion class]]) {
CLBeaconRegion *beaconRegion = (CLBeaconRegion *)region;
int major = [beaconRegion.major intValue];
int minor = [beaconRegion.minor intValue];
NSLog(#" Major %# Minor %#", beaconRegion.major, beaconRegion.minor);
}
}
The region monitoring callback you have implemented will not tell you the individual identifiers of the beacons you detect. If you want to get the identifiers for individual beacons detected, you have to use the beacon ranging APIs as #Larme says in his comment. The callback for ranging includes a second parameter that is an array of all beacons seen.
You have to differentiate between Monitoring and Ranging iBeacons. Only successfully ranging iBeacons provides you with the Major/Minor IDs.
Looks like you are not initializing the BeaconRegion with minor and major values
While initializing the beacon region you need to use
initWithProximityUUID:major:minor:identifier:
instead of
initWithProximityUUID:identifier:
If you do not want to initialize minor and major values into regions, then you may want to call didRangeBeacons method as mentioned in the comments.
you're trying to get region's major and minor values. but you say that want to get the beacon's values.
it depends on which beacon brand you're using but there must be a method that returns the beacons array the device has found. generally the first object of the array is the closest one.
in that method you can get the beacon's values.
an example code:
- (void)beaconManager:(ESTBeaconManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(ESTBeaconRegion *)region
{
if([beacons count] > 0)
{
// beacon array is sorted based on distance
// closest beacon is the first one
ESTBeacon* closestBeacon = [beacons objectAtIndex:0];
NSString* theMajor = [NSString stringWithFormat:#"%#",closestBeacon.major];
}
}
Setup locationManager (remember to configure plist for iOS 8 to add in these 2 values NSLocationAlwaysUsageDescription, NSLocationWhenInUseUsageDescription).
#property (strong, nonatomic) CLLocationManager *locationManager;
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
[[UIApplication sharedApplication] cancelAllLocalNotifications];
// Needed for iOS 8
if([self.locationManager respondsToSelector:#selector(requestAlwaysAuthorization)]) {
[self.locationManager requestAlwaysAuthorization];
}
Then call startRangingItem:
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region{
if ([region isKindOfClass:[CLBeaconRegion class]]) {
[self startRangingItem];
}
}
- (void)startRangingItem {
CLBeaconRegion *beaconRegion = [self beaconRegionWithItem];
[self.locationManager startRangingBeaconsInRegion:beaconRegion];
}
- (CLBeaconRegion *)beaconRegionWithItem{
NSUUID *iPadTransmitterUUID = [[NSUUID alloc] initWithUUIDString:#"AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEFFFFF1"];
CLBeaconRegion *beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:iPadTransmitterUUID identifier:#"Transmitter1"];
return beaconRegion;
}
Then in didRangeBeacons:***
- (void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region{
CLBeacon *testBeacon = [beacons objectAtIndex:0];
NSLog(#"Inside didRangeBeacons Major %# Minor %#", testBeacon.major, testBeacon.minor);
}
***Please note that this didRangeBeacons method will only run in background for 5 seconds once the user enter the region. It will stop ranging after 5 seconds. If you want to continue ranging, the user needs to launch the app. (forcing the didRangeBeacons to run in background is possible, but it might get rejected by apple)
I'm trying to use UUIDs as identifiers for specific beacons (using phones in this case). I understand that major and minor are used to do this, but I'd rather use the UUID, or the identifier string.
With that being said, is there anyway to scan for beacons regardless of UUIDs with the CLBeacon API?
On Android you can scan for all UUIDs. On iOS you cannot. See:
http://developer.radiusnetworks.com/2013/10/21/corebluetooth-doesnt-let-you-see-ibeacons.html
On iOS, CoreLocation limits you to monitoring for at most 20 different UUIDs. Ranging does not have a limit of 20, but you still must know the UUIDs ahead of time.
As far as I know, there is no restriction in monitoring iBeacon regions with different UUID. You can do something like:
NSArray *uuids = [NSArray arrayWithObjects:#"####-####-###1", #"####-####-###2", nil];
for (NSString *uuidString in uuids) {
CLBeaconRegion *region = [[CLBeaconRegion alloc] initWithProximityUUID:[[NSUUID alloc] initWithUUIDString:uuidString] identifier:identifier];
region.notifyOnEntry = entry;
region.notifyOnExit = exit;
region.notifyEntryStateOnDisplay = YES;
[_locationManager startMonitoringForRegion:region];
}
You just need to make sure you check for the UUID in you locationManager's delegate methods. You can use something like this:
- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region {
if ([region isKindOfClass:[CLBeaconRegion class]]) {
CLBeaconRegion *beaconRegion = (CLBeaconRegion *)region;
NSLog(#"ProximityUUID:%#", beaconRegion.proximityUUID);
NSLog(#"ProximityMajor:%#", beaconRegion.major);
NSLog(#"ProximityMinorD:%#", beaconRegion.minor);
}
}
You should see that the delegate method is called with different UUIDs.
Hope this helps.
I have a location based app, all though the region is correct the app never moves to didEnterRegion or didExitRegion
for (int x = 0; x <= [[[TaskStore sharedStore] allTasks]count]-1; x++)
{
NSArray *tasks = [[TaskStore sharedStore] allTasks];
Task *selectedTask = [tasks objectAtIndex:x];
location.latitude = selectedTask.locationCoord.coordinate.latitude;
location.longitude = selectedTask.locationCoord.coordinate.longitude;
NSString* desiriedLoc = [selectedTask locationName];
CLRegion *region = [[CLRegion alloc] initCircularRegionWithCenter: location radius: 30.0 identifier: desiriedLoc];
NSLog(#"Entered new Location in Region %#", region);
[locManager startMonitoringForRegion:region];
}
-(void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
NSLog(#"didEnterRegion for %#",region.identifier);
UIAlertView *alr=[[UIAlertView alloc] initWithTitle:#"Reminder didEnterRegion"
message:region.identifier delegate:nil cancelButtonTitle:nil otherButtonTitles:#"Ok",nil];
[alr show];
}
-(void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region
{
NSLog(#"didExitRegion for %#",region.identifier);
UIAlertView *alr=[[UIAlertView alloc] initWithTitle:#"Reminder didExitRegion" message:region.identifier delegate:nil cancelButtonTitle:nil otherButtonTitles:#"Ok",nil];
[alr show];
}
Here is a string print out from my location:
Entered new Location with the coordinates Latitude: 51.509980 Longitude: -0.133700
and here is a string print out from the region:
Entered new Location in Region (identifier London) <+51.50998000,-0.13370000> radius 30.00m
As #verbumdei commented, the only way you will get the -didEnterRegion and -didExitRegion is to establish a CLLocationManagerDelegate. Set your view as the delegate and add those methods and you should see the update.
One thing to note, you are using a 30M radius, you will need to be quite approximate in your location if you want to trigger updates. This is fairly easy to do in the Simulator, but in real life usage (on device), 30M accuracy is a bit tougher. I would start with 100M and work your way down based on experience.
If you start monitoring while staying in the target region, nothing triggered.
Because it's not really an "didEnterRegion" event.
You don't seem to be setting
region.notifyOnEntry = YES;
region.notifyOnExit = YES;
Without explicitly setting these properties, the specified events will not get fired.