Updated to Swift. Please visit here for the tutorial using Swift.
At WWDC earlier this year (2013) Apple announced iOS 7. Along with that announcement we heard of new API called iBeacons. iBeacons is a technology that uses transmitters and receivers which can let your iOS device know how far you are away from a beacon. One device acts as the transmitter and the other is a receiver.
iBeacon transmitters can either be a dedicated hardware device running Bluetooth 4.0 LE or you can configure a compatible iPhone or iPad to act as a transmitter. The receiving device is a compatible iPhone or iPad that can receive Bluetooth 4.0 LE. Compatible devices include the iPhone 4S and newer and the iPad 3 and newer (including iPad mini).
To give an example of what you could use iBeacons for, think of a group of museums managed by a single company. Each room in each of the museums would have an iBeacon transmitter placed in them. Each beacon is configured with settings or what Apple refers to as an identity. If you are running an app provided by the museum, each time you enter the museum you will be presented with information which changes depending on what room you are in.
There are three settings that set a transmitters identity:
proximityUUID is a property which is unique to each company. In the case of the museum company, the same UUID would be given to all beacons. You generate a UUID by loading up the terminal on a Mac and entering uuidgen. It provides a UUID such as “23542266-18D1-4FE4-B4A1-23F8195B9D39”.
major is the property that you use to specify a related set of beacons. This is a numeric value such as 1 could mean the museum in London, England and 2 could mean the museum in Leeds, England. All beacons in a location are assigned the correct value for major according to the location… so all beacons in Leeds would have the major property set to 2.
minor in this case would be used to specify a particular beacon in a museum. In our example, we could use minor to set the room number in the building.
When an iPhone is running an app (made by the museum) it can be programmed to look for the museums UUID. When it detects it, it looks at the major and minor values and can determine which of the museums you are in as well as the room the you are in.
So when the app detects you have entered the region (AKA, picked up a transmitter) containing the museum UUID of “21541166-18D1-4FE4-B4A1-26F8135B3D31” and a major of 1 and minor of 14, it knows you are in the London museum and in room 14 of that museum. The app could then provide extra information such as video presentations at the art work in the room, or perhaps show you extra information in video/audio or text form about a particular piece.
There are many ways you can work with iBeacons. Indoor navigation could be one possibility. Think of a busy airport kitted out with hundreds of beacons each telling you exactly where you are and need to go to get to your gate. Think of shops that could put them around the shelves so you can more easily find products. Think of the abilities that iBeacons have to let you know you are within very close proximity of a transmitter (similar to RFID) so that doors could be unlocked or payments could be processed.
With this in mind, I want to do a quick tutorial showing how you can set up an iOS device as a transmitter and set up another as a receiver. Note that the requirements are:
1. You must be enrolled in the iOS developer program ($99/year). This tutorial requires you to run the test app on two iOS devices.
2. You must have 2 iOS devices which have Bluetooth 4.0. This includes the iPhone 4S and newer, iPad 3 and newer and the iPad mini.
3. You need a Mac of course, running Xcode.
You can check out our iOS tutorials on this site, DefFright.
iBeacons Tutorial
The first part of this tutorial will have us set up a single view application. When done, set up the storyboard similar to what you see below.
The first View Controller can be embedded in a Navigation Controller by clicking on it, clicking Editor > Embed In > Navigation Controller.
I put two buttons in there. One called Track Beacon and the other Transmit Beacon. I dragged out two more view controllers (top right and bottom right) and CRTL+dragged from each button to one of the view controllers and configured them as a push segue.
For the Track View Controller and Config View Controller, I created a custom class for each and assigned them to the view controllers.
When this is set up, drag all the UILabels and buttons in place.
For the Track View Controller, I CTRL+Dragged all the labels on the right to the following IBOutlets:
@property (weak, nonatomic) IBOutlet UILabel *beaconFoundLabel;
@property (weak, nonatomic) IBOutlet UILabel *proximityUUIDLabel;
@property (weak, nonatomic) IBOutlet UILabel *majorLabel;
@property (weak, nonatomic) IBOutlet UILabel *minorLabel;
@property (weak, nonatomic) IBOutlet UILabel *accuracyLabel;
@property (weak, nonatomic) IBOutlet UILabel *distanceLabel;
@property (weak, nonatomic) IBOutlet UILabel *rssiLabel;
For the Config View Controller, I added the following IBOutlets in the same way:
@property (weak, nonatomic) IBOutlet UILabel *uuidLabel;
@property (weak, nonatomic) IBOutlet UILabel *majorLabel;
@property (weak, nonatomic) IBOutlet UILabel *minorLabel;
@property (weak, nonatomic) IBOutlet UILabel *identityLabel;
Configuring the Main View Controller
As we only do a push segue from buttons to other view controllers, the ViewController.h and .m files do not need to be modified.
Setting up an iBeacon Transmitter
For this part of the tutorial, we are going to focus on setting up an iOS device as a transmitter.
To set up a transmitter we need the following frameworks importing:
CoreBluetooth.framework and CoreLocation.framework. Add these in the linked frameworks and libraries section.
After these have both been imported, we then need to import each of them in to our ConfigViewController header file:
#import
#import
Next, we need to add some properties to work with:
@property (strong, nonatomic) CLBeaconRegion *beaconRegion;
@property (strong, nonatomic) NSDictionary *beaconPeripheralData;
@property (strong, nonatomic) CBPeripheralManager *peripheralManager;
Line 1 above, we create a CLBeaconRegion property called beaconRegion. This property is used to define the settings (proximityUUID, major and minor) that are needed to set up a beacon as a transmitter.
Line 2 we create an NSDictionary property which contains the peripheral data of the beacon.
Line 3, we create the CBPeripheralManager property where the methods are contained to start it transmitting.
Switching over to the implementation file, there are several things we need to do to get an iOS device transmitting a signal.
In this tutorial, I am using 4 methods as well as the viewDidLoad method to get the transmitter working. viewDidLoad contains method calls to initBeacon and setLabels. initBeacon is used to configure the beacon. Lets look at that first.
- (void)initBeacon {
NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:@"23542266-18D1-4FE4-B4A1-23F8195B9D39"];
self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid
major:1
minor:1
identifier:@"com.devfright.myRegion"];
}
On line 2 we set up an NSUUID by initialising it with an NSString. In this case, I opened up the terminal on my mac and typed in uuidgen and then pasted the provided UUID in the initialiser. You can generate UUIDs from code if you wish, but it isn’t really necessary in this case.
Line 3 we alloc and init the self.beaconRegion property and call the initWithProximityUUID:major:minor:identifier: method. In this case, I hard coded the settings other than UUID which was pulled in from an NSUUID object. I just assigned 1 for major, 1 for minor and an identifier string of com.devfright.myRegion.
Moving on, we now have a transmitBeacon method which is an IBAction of a button on the view controller on the storyboard. The method looks like this:
- (IBAction)transmitBeacon:(UIButton *)sender {
self.beaconPeripheralData = [self.beaconRegion peripheralDataWithMeasuredPower:nil];
self.peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self
queue:nil
options:nil];
}
On line 2 we call peripheralDataWithMeasuredPower on the self.beaconRegion property. This method returns an NSDictionary. We assign the result to our NSDictionary property which is called beaconPeripheralData.
On line 3 we alloc/init the peripheralManager property. Here we are instructed by Apple to call initWithDelegate:queue:options. We just provide nil and nil for the queue and options and set the delegate to self.
When we set the delegate to self in that initialiser, we will get a warning. To fix that, we need to make sure the class conforms to the CBPeripheralManagerDelegate protocol. Add the delegate on to the end of the @interface line in the header as below:
@interface ConfigViewController : UIViewController
When we adopt this protocol, we are then given another warning which requires we implement a particular method from the delegate. Lets look at that now:
-(void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {
if (peripheral.state == CBPeripheralManagerStatePoweredOn) {
NSLog(@"Powered On");
[self.peripheralManager startAdvertising:self.beaconPeripheralData];
} else if (peripheral.state == CBPeripheralManagerStatePoweredOff) {
NSLog(@"Powered Off");
[self.peripheralManager stopAdvertising];
}
}
The required method is peripheralManagerDidUpdateState:. I implemented it with the code listed above.
To power on the transmitter, we need to call the startAdvertising: method. If we do this in the transmitBeacon method above, it will likely fail because the Bluetooth services will not be active in time for it to be called and you will get a message logged saying “CBPeripheralManager is not powered on”. Instead, we put that code in the peripheralManagerDidUpdateState method. As soon as the Bluetooth status changes, the delegate calls this method and we can then check to see if it powered on and if so, startAdvertising the beacon.
Line 2 above checks the state and if it is powered on CBPeripheralManagerStatePoweredOn, we can start advertising with the NSDictionary that we created earlier. If not, we can stop Advertising or just do nothing.
Finally, we set the labels to show us what is being transmitted. I call this method from the viewDidLoad method:
- (void)setLabels {
self.uuidLabel.text = self.beaconRegion.proximityUUID.UUIDString;
self.majorLabel.text = [NSString stringWithFormat:@"%@", self.beaconRegion.major];
self.minorLabel.text = [NSString stringWithFormat:@"%@", self.beaconRegion.minor];
self.identityLabel.text = self.beaconRegion.identifier;
}
Here, we are simply setting the text property of each of the labels on the view to show the details of what the beacon has been configured to be. Note that the proximityUUID has another property called UUIDString which can be accessed to set the uuidLabel.text property. The identifier is a string also, which means we can assign it as it is. The only different things we need to do is convert major and minor to string with the stringWithFormat class method.
Our app is now capable of transmitting a beacon although its kind of boring if we leave it there. In the next part, we are going to look at how our other iOS device can be set up to be made aware when it is in the vicinity of the transmitter.
Receiving iBeacon Information
Detecting iBeacons is done through the Core Location Framework. The updated framework for iOS 7 can now recognise when you enter the region of a beacon and when doing so, you can then range the beacon to find out information about it.
Lets begin here with the TrackViewController header file. We first need to import the CoreLocation framework:
#import
We then need to add some properties as follows:
@property (strong, nonatomic) CLBeaconRegion *beaconRegion;
@property (strong, nonatomic) CLLocationManager *locationManager;
Line 1 is a CLBeaconRegion property which is used to define which beacons we are looking for. ie, going back to the museum example we would only want to look for beacons which have the exact same UUID.
Line 2 we add a locationManager property which is used for setting up location services and allowing the beacons to be found.
In the viewDidLoad method we add the following:
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
[self initRegion];
Line 1 we alloc/init the locationManager and line 2, we set delegate to self for the locationManager. This of course requires that we adopt the CLLocationManagerDelegate protocol. Add this to the interface line to your header:
@interface TrackViewController : UIViewController
For more information about this delegate protocol, check out our CLLocationManagerDelegate tutorial.
Back to the viewDidLoad method, we then call a method I created called initRegion.
Lets now look at initRegion.
- (void)initRegion {
NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:@"23542266-18D1-4FE4-B4A1-23F8195B9D39"];
self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid identifier:@"com.devfright.myRegion"];
[self.locationManager startMonitoringForRegion:self.beaconRegion];
}
Line 2 we create an NSUUID object and alloc/init with a string. This string should match the one you created for the transmitter for this one to work.
On line 3 we alloc/init the self.beaconRegion property. The initialiser we use is initWithProximityUUID:identifier and we specify our UUID and our identifier. In this case, I just hard coded the identifier in (copied and pasted the NSString from the transmitter.
We then need to start monitoring this particular beaconRegion. We do that on line 4.
Moving on, we now need to configure the app to detect when it enters or exits a particular beacon region. We do that with the following two methods from the delegate:
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region {
[self.locationManager startRangingBeaconsInRegion:self.beaconRegion];
}
-(void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region {
[self.locationManager stopRangingBeaconsInRegion:self.beaconRegion];
self.beaconFoundLabel.text = @"No";
}
The first method is the didEnterRegion: method. When you start picking up the transmitter (ie, you are close enough to it), this method is called. What we do here is then call the startRangingBeaconsInRegion: method on locationManager. We specify the beacon region that we created earlier in this class (self.beaconRegion).
On the second method, we do exactly the same bit we tell the app to stopRangingBeaconsInRegion: instead.
Lets take a look at the didRangeBeacons method.
-(void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region {
CLBeacon *beacon = [[CLBeacon alloc] init];
beacon = [beacons lastObject];
self.beaconFoundLabel.text = @"Yes";
self.proximityUUIDLabel.text = beacon.proximityUUID.UUIDString;
self.majorLabel.text = [NSString stringWithFormat:@"%@", beacon.major];
self.minorLabel.text = [NSString stringWithFormat:@"%@", beacon.minor];
self.accuracyLabel.text = [NSString stringWithFormat:@"%f", beacon.accuracy];
if (beacon.proximity == CLProximityUnknown) {
self.distanceLabel.text = @"Unknown Proximity";
} else if (beacon.proximity == CLProximityImmediate) {
self.distanceLabel.text = @"Immediate";
} else if (beacon.proximity == CLProximityNear) {
self.distanceLabel.text = @"Near";
} else if (beacon.proximity == CLProximityFar) {
self.distanceLabel.text = @"Far";
}
self.rssiLabel.text = [NSString stringWithFormat:@"%i", beacon.rssi];
}
On line 2, we create a CLBeacon object called beacon. We alloc/init it and then on the next line, we pull the last object from the argument in the method (beacons) and store that in beacon. We now have a beacon to work with.
The next few lines we are simply pulling information from the beacon such as its UUIDString, major and minor values, accuracy and RSSI. These will change depending on what the transmitter is sending. If you modify the transmitter code to set minor to 2, this CLBeacon will contain a 2 for minor and so on.
The accuracy, proximity and RSSI are all properties of the beacon which help us determine distance from the beacon. The accuracy will constantly change depending on interference. The proximity provides 4 values. Unknown, immediate, near, far with immediate being about half a meter away at most. Near is a little further away and far is several meters away.
RSSI is the signal strength in decibels.
Running the App
The app is now ready to run. You need to run it on two compatible devices and set one up to transmit and the other one to receive.
What you will notice is that you need to walk a distance away (10 – 20 meters) from the transmitter and then walk back towards it for this to work. When you “enter the region” the didRangeBeacons is called by the didEnterRegion method and the CLBeacon is pulled from the provided beacons NSArray and the information is then put to the view on the screen.
Note that we are only pulling the lastObject from the beacons NSArray. The reason for this is that we are only testing with a single beacon. If we were to have multiple beacons, we would want to query all beacons in the NSArray to figure out where we are in proximity to each one.
Testing Without Entering or Exiting the Region
If you don’t want to leave your home briefly each time you want to test, there’s a quick (and shoddy) work around. Add the following to viewDidLoad:
[self locationManager:self.locationManager didStartMonitoringForRegion:self.beaconRegion];
Add this method somewhere in the code:
- (void)locationManager:(CLLocationManager *)manager didStartMonitoringForRegion:(CLRegion *)region {
[self.locationManager startRangingBeaconsInRegion:self.beaconRegion];
}
What we are doing is manually calling the didStartMonitoringForRegion method and providing it with the locationManager and defined beaconRegion to work with. It isn’t a great implementation but it works enough for you to do some testing.
The full code can be found below or you can download the project (needs Xcode 5 and devices running iOS 7) from here.
ConfigViewController Header
#import
#import
#import
@interface ConfigViewController : UIViewController
@property (strong, nonatomic) CLBeaconRegion *beaconRegion;
@property (weak, nonatomic) IBOutlet UILabel *uuidLabel;
@property (weak, nonatomic) IBOutlet UILabel *majorLabel;
@property (weak, nonatomic) IBOutlet UILabel *minorLabel;
@property (weak, nonatomic) IBOutlet UILabel *identityLabel;
@property (strong, nonatomic) NSDictionary *beaconPeripheralData;
@property (strong, nonatomic) CBPeripheralManager *peripheralManager;
@end
ConfigViewController Implementation
#import "ConfigViewController.h"
@interface ConfigViewController ()
@end
@implementation ConfigViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[self initBeacon];
[self setLabels];
}
- (void)initBeacon {
NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:@"23542266-18D1-4FE4-B4A1-23F8195B9D39"];
self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid
major:1
minor:1
identifier:@"com.devfright.myRegion"];
}
- (void)setLabels {
self.uuidLabel.text = self.beaconRegion.proximityUUID.UUIDString;
self.majorLabel.text = [NSString stringWithFormat:@"%@", self.beaconRegion.major];
self.minorLabel.text = [NSString stringWithFormat:@"%@", self.beaconRegion.minor];
self.identityLabel.text = self.beaconRegion.identifier;
}
- (IBAction)transmitBeacon:(UIButton *)sender {
self.beaconPeripheralData = [self.beaconRegion peripheralDataWithMeasuredPower:nil];
self.peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self
queue:nil
options:nil];
}
-(void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {
if (peripheral.state == CBPeripheralManagerStatePoweredOn) {
NSLog(@"Powered On");
[self.peripheralManager startAdvertising:self.beaconPeripheralData];
} else if (peripheral.state == CBPeripheralManagerStatePoweredOff) {
NSLog(@"Powered Off");
[self.peripheralManager stopAdvertising];
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
TrackViewController Header
#import
#import
@interface TrackViewController : UIViewController
@property (weak, nonatomic) IBOutlet UILabel *beaconFoundLabel;
@property (weak, nonatomic) IBOutlet UILabel *proximityUUIDLabel;
@property (weak, nonatomic) IBOutlet UILabel *majorLabel;
@property (weak, nonatomic) IBOutlet UILabel *minorLabel;
@property (weak, nonatomic) IBOutlet UILabel *accuracyLabel;
@property (weak, nonatomic) IBOutlet UILabel *distanceLabel;
@property (weak, nonatomic) IBOutlet UILabel *rssiLabel;
@property (strong, nonatomic) CLBeaconRegion *beaconRegion;
@property (strong, nonatomic) CLLocationManager *locationManager;
@end
TrackViewController Implementation
#import "TrackViewController.h"
@interface TrackViewController ()
@end
@implementation TrackViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
[self initRegion];
[self locationManager:self.locationManager didStartMonitoringForRegion:self.beaconRegion];
}
- (void)locationManager:(CLLocationManager *)manager didStartMonitoringForRegion:(CLRegion *)region {
[self.locationManager startRangingBeaconsInRegion:self.beaconRegion];
}
- (void)initRegion {
NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:@"23542266-18D1-4FE4-B4A1-23F8195B9D39"];
self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid identifier:@"com.devfright.myRegion"];
[self.locationManager startMonitoringForRegion:self.beaconRegion];
}
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region {
NSLog(@"Beacon Found");
[self.locationManager startRangingBeaconsInRegion:self.beaconRegion];
}
-(void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region {
NSLog(@"Left Region");
[self.locationManager stopRangingBeaconsInRegion:self.beaconRegion];
self.beaconFoundLabel.text = @"No";
}
-(void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region {
CLBeacon *beacon = [[CLBeacon alloc] init];
beacon = [beacons lastObject];
self.beaconFoundLabel.text = @"Yes";
self.proximityUUIDLabel.text = beacon.proximityUUID.UUIDString;
self.majorLabel.text = [NSString stringWithFormat:@"%@", beacon.major];
self.minorLabel.text = [NSString stringWithFormat:@"%@", beacon.minor];
self.accuracyLabel.text = [NSString stringWithFormat:@"%f", beacon.accuracy];
if (beacon.proximity == CLProximityUnknown) {
self.distanceLabel.text = @"Unknown Proximity";
} else if (beacon.proximity == CLProximityImmediate) {
self.distanceLabel.text = @"Immediate";
} else if (beacon.proximity == CLProximityNear) {
self.distanceLabel.text = @"Near";
} else if (beacon.proximity == CLProximityFar) {
self.distanceLabel.text = @"Far";
}
self.rssiLabel.text = [NSString stringWithFormat:@"%i", beacon.rssi];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
Daniel McCarthy says
nice post. I can’t find much documentation on iBeacons. I assume monitoring for beacons in range is supported when the app is running in the background, but will a device that is transmitting as the actual ibeacon continue to transmit while the app is in the background?
MatthewN says
Good question. I’m not 100% sure without testing more, but this article talks about background modes for peripherals and allowing it to advertise while in the background.
https://developer.apple.com/library/ios/documentation/NetworkingInternetWeb/Conceptual/CoreBluetooth_concepts/CoreBluetoothBackgroundProcessingForIOSApps/PerformingTasksWhileYourAppIsInTheBackground.html
I’ll need to run tests to see what happens. It indicates that transmissions can still happen, but that the state changes. It also notes that if memory needs to be freed up then it can stop transmitting, so my thoughts are that it wont work 100% of the time in the background although if region monitoring does then perhaps Apple has allowed iBeacons to be transmitted without being interrupted. I’ll post back when I know more.
Tobias Wöss says
Hello! Great post, did help a lot, thanks!
I’ve now managed to Transmit and Track Beacons. Also, my “tracker” discovers nearby beacons whenever I turn on the devices screen (I had to add [self.beaconRegion setNotifyEntryStateOnDisplay:YES]; to the initRegion of the tracker). Yet, what is missing, is the ability to keep the “transmitter” advertising whilst in background.
As you’ve mentioned, there could/should be a way to make this happen – but so far, I didn’t succeed either.
The only thing I’ve found in the docs on this issue, was in the CBPeripheralManager-Reference. Where under “startAdvertising” you can find the following text “While your app is in the background, the local name is not advertised and all service UUIDs are placed in the overflow area. The exact format of advertising and response data is defined in the Bluetooth 4.0 specification, Volume 3, Part C, Section 11.” But I have no clue what to do with this info. It also says, that data stored there is transmitted “best effort”, but in my case, it’s not transmitted at all or the tracker simply doesn’t recognize it.
MatthewN says
I think an info.plist entry called bluetooth-peripheral might sort it. Although it says it can transmit (as you mentioned in your 3rd paragraph), the risk is when the app needs to kill the app due to memory. This can stop it transmitting. I wonder if there’s a way to re-wake the application if this happens… perhaps some sort of region monitoring (didEnterLocation) or something that could re-trigger the app to launch again in the background, or perhaps even background processing could be used.
I certainly need to test more.
KevinN says
Hi Matthew and Tobias, were you able to sort this out? I’m struggling with CBPeripheralManager without any outcomes. Thank you in advance!!
MatthewN says
Hello Kevin. I haven’t had a chance to test much more with this. What results are you seeing with your tests? I need to open up the project again and take another look to see if I can keep an iBeacon alive as a transmitter.
ZeroPants says
Thanks for the tutorial. I notice that when I run your app in Transmit mode on my iPhone 4S running iOS7 and then read the CBPeripheral using an iPhone 5s running iOS7, the CBPeripheral identifier value is a different value than what you you hardcoded into the CBBeaconRegion you set in the Central. Do you know why that is?
MatthewN says
I’ll test that right now and let you know in a few moments.
MatthewN says
Which part of the code is this found?
I just had a quick read about the identifiers specified in the init methods for the CLBeaconRegion and these are just so you can distinguish between different beacons. I don’t believe this information is transmitted from transmitter to receiver (just the UUID, major and minor).
ZeroPants says
I had thought that a transmitting CBPeripheralManager (beacon) initialized with a certain UUID would broadcast that same UUID to any listening Centrals so that the Central could pull it off its CBPeripheral object and dynamically use that to monitor range of the beacon. However, what I am understanding from my own testing is that the UUID of the CBPeripheral pulled by the Central is not the same UUID that is initialized on the transmitting CBPeripheralManager.
MatthewN says
The UUID’s are just used internally so that the region monitoring can be triggered when needed.
For example, Walmart would have its own UUID that is transmitted on each of its beacons. You then have a Walmart app that is configured to listen out only for Walmart beacons.
You init the beaconRegion with the line below (found in the initRegion method).
[self.locationManager startMonitoringForRegion:self.beaconRegion];
That self.beaconRegion was configured earlier with a matching UUID to “Walmart”.
So your transmitter is saying “Hi, I’m a walmart beacon” and your receiver is saying “I’m looking only for Walmart Beacons”.
When a match is found, it triggers the didEnterRegion (or didExitRegion) delegate methods and at that point we start ranging the beacon (if we entered the region).
We then can inspect various properties of the beacon. UUID is transmitted, but only received if you init it with a matching UUID on the receiver. If either of them change then the beacon would not be detected.
Hopefully this will also help clear it up:
Your CLBeaconRegion is the beacon object which contains UUID, major and minor properties.
CLBeacon is the beacon that is created when a beacon is detected. This will match the CLBeaconRegion although UUID is only used to ensure there is a match and if there is, it can be detected.
CBPeripheralManager is created and is responsible for transmitting the CLBeaconRegion with the startAdvertising method. You previously put the CLBeaconRegion in to an NSDictionary and you start advertising the beacon within the dictionary.
You also create a CLBeaconRegion to look out for on your receiver. You have 3 init methods for a CLBeaconRegion.
initWithProximityUUID:identifier:
initWithProximityUUID:major:identifier:
initWithProximityUUID:major:minor:identifier:
If you use the first one (just UUID) then it will look for any transmitting beacon with a matching UUID.
If you use the second one, it will look for all matching UUIDs and only those beacons that match the major property.
If you use the third one, it will only show beacons that are transmitting the specified UUID, major and minor values and will ignore the rest.
What you will see is that if you manually change the transmitting UUID (or the receiving UUID) to something different then it will never pick up the iBeacon transmitter (because there will be no match).
If you change the major or minor values then the receiver will pick these up and it will update the label on the receiver page accordingly.
Hopefully this helps, but please keep asking away if not :)
Saqib Khan says
Thank you for such a good tutorial.
Matthew i have two questions for you i have run your code in iPad4 device as the beacon and iphone5 as the receiver. I notice two things with the SDK
1) Even though i am in the same position, delegate method(didRaneBeaconsInRegion) is getting called frequently with different distances. Basically its not getting stable even though the user is in the same position.
2) When i move with the device, the distance between the beacon and the application is showing a wrong values often.I need to know whether can i rely on the distance or completely on the proximity?
I’ll really appreciate any help from your side.
Thanks and Regards
Saqib Khan
MatthewN says
Hello Saqib,
From my understanding, the beacon accuracy is interrupted by interference. Right now I’m testing and getting an accuracy of around 0.1 to 0.2 meters which fluctuates by the second. I think due to the amount of interference around us, it would probably always be the case.
My example screenshot above (the screenshot) can be a bit confusing (I’ll switch it over now). Where I have distance listed in the Tracking Beacon view, I should really switch proximity so that it is Immediate, Near or Far. To get a true distance, you would need to calculate it based on the RSSI. If your transmitter is stationary (like it would be if you used a dedicated iBeacon device stuck to a wall in a museum for example), then you would specify the RSSI at 1 meter away to make it more accurate. To do that, modify this line:
self.beaconPeripheralData = [self.beaconRegion peripheralDataWithMeasuredPower:nil];
The argument which is currently set to nil can be set to the RSSI at 1M away (Needs to be specified as an NSNumber). This would look like this:
self.beaconPeripheralData = [self.beaconRegion peripheralDataWithMeasuredPower:[NSNumber numberWithInteger:-53]];
My next test is going to include 3 transmitters to see if I can triangulate position in a room based on the signal strength. I want to know how accurate this can be. According to my accuracy level, it is currently within 0.1 – 0.2 meters which is pretty good when compared to GPS.
Saqib Khan says
Hi MatthewN,
I have change the mentioned line
from this self.beaconPeripheralData = [self.beaconRegion peripheralDataWithMeasuredPower:nil];
to this self.beaconPeripheralData = [self.beaconRegion peripheralDataWithMeasuredPower:[NSNumber numberWithInteger:-53]];
and have put both the devices on my desk and after running the application i NSLog the values of RSSI and Accuracy, my RSSI value fluctuates between -43 to -51 and accuracy value fluctuates between 0.15meter to 0.30meter both the parameters keep on changing between the respective ranges.
Is this a normal behaviour of iBeacons or am i making any mistake.
Note: I haven’t change any line of your sample code. I am just running your sample code.
Thanks and Regards
Saqib Khan
MatthewN says
Can you measure 1M between devices and see what the RSSI is and then use that in place of the -53? To do that, you will need to transmit nil first and then update nil with whatever value you get.
Saqib Khan says
For 1M the RSSI i get is -55. But still the same, my RSSI and Accuracy value keep on fluctuating even after i changed the peripheralDataWithMeasuredPower to -55 and kept both the device are on desk.
Saqib Khan says
For 1M the RSSI i get is -55. But still the same, my RSSI and Accuracy value keep on fluctuating even after i changed the peripheralDataWithMeasuredPower to -55 and kept both the device are on desk.
MatthewN says
I’m starting to get the impression that the delegate will always constantly be called due to the interference in the area around you. Could you try away from a computer to see if you see the same effects? I haven’t tested in this much depth yet, but I think that we might always see fluctuations happen and that we’ll need some logic to suppress some of it. ie, a mixture of the accuracy and distance moved (by translating RSSI in to distance). If the “distance moved” is less than the accuracy then just ignore it.
So taking your example of -55 as the RSSI at 1M away we could then move 5 meters away and see what RSSI is and then get a good indication of RSSI per meter. We could then calculate that you are perhaps 3.2 meters away from the transmitter and if accuracy is 0.2 meters then if there is a variance detected of less than 0.2 meters, just skip over any code.
I think this gives opportunity to be creative with what numbers we get. I’d first suggest testing in different environments close to and away from electronic equipment like a desktop, TV, microwaves etc… and then also test through walls etc…
Saqib Khan says
Yes MatthewN you are rite the delegate
-(void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region
keep on calling again and again even if the device is not moving.
MatthewN says
I’ve seen similar behaviour on the CLLocationManager when didUpdateLocations is called repeatedly. I guess that’s just the nature of tracking location. Even a slight variation would trigger the delegate methods to be called.
Saqib Khan says
MatthewN i measure the value of RSSI for 20 meters and the value is -80 i pass this value in peripheralDataWithMeasuredPower now in my receiver device i am getting accuracy value which is fluctuating between 0.1 to 0.2 meter but for -80 RSSI i am not getting correct accuracy (value of accuracy get stable but with wrong distance)
and reply for your last comment when device is not moving than why didRangeBeacons is called again and again there is not even a slightly variation in location.
MatthewN says
There is no physical movement of the devices, but due to interference which is all around us, the signal that is sent out is regularly changing. I think to not have this happen you’d need to be sat in a completely isolated room which blocks out any radio frequencies. If not then there will always be other external conditions that rapidly effect signal. This is why the delegate method is constantly called. Any variation to the ranging is detected and thus called. It would be up to you as the developer to decide if the change was significant enough to do anything with it.
MatthewN says
I should add that because this method is regularly called you should keep it light weight. I strongly recommend some if/then type logic to ensure that any work that you are doing is only done when it needs to. If someone is still then there’s no need to update and tell them that there is interference. Just ignore interference and update when the move further than the accuracy.
Saqib Khan says
MatthewN i need your suggestion i have put the RSSI equal to -80 tand the accuracy value is stable means now it’s fluctuates in between 0.1 to 0.2 meters but the accuracy value is not correct it showing 1 meter accuracy value for the 20 meter distance. can we skip the value of accuracy and jst rely on the conditions of immediate , near and far or the immediate , near and far values also rely on accuracy value
MatthewN says
You can just use nil if you wish for the accuracy (as it is in the tutorial).
You are free to use which ever values suit the type of application you are trying to make. If it isn’t confidential then feel free to share your idea and I’ll provide some input on what I think will work best.
Saqib Khan says
Actually i have to use this thing in Library where different Beacons devices is already fix when user go there than application detect the ID and give the alert to the user that this section of library contain which types of books but there would be lots of Computer over there so i can ignore the interface of the electronic devices.
MatthewN says
I think Immediate, Near and Far values would suffice here. Depending on how far the beacons are apart, you might want to switch off ranging once the beacon has been found. When you exit the region and enter another region you could re-enable ranging.
If your beacons are all close by then you’d need to use the NSArray of beacons provided (rather than pulling out just the last object) and scan through them all to find which one is closest (or which has the highest RSSI value). If that changes it means you are closer to another beacon and at that point you then switch the view for the user or push out a notification etc…
Saqib Khan says
Thank you for all you co-operation. I really appreicate this :)
Mike Berlin says
I’d love to hear about your findings on the triangulation. That would be very useful indeed! Also, are you aware of how to calculate meters based on the RSSI?
MatthewN says
I’ve not attempted the calculations on RSSI to meters yet. All I know is that the further away the number changes and when you move back it changes again :) This along with the triangulation should make for a good test to see what is possible. I’ve just finished off another tutorial now, so will be starting on the triangulation next… it will probably take a few days to post my findings (maybe Tuesday/Wednesday).
Noah Bass says
Did you ever get anywhere with triangulation?
Hernando says
Hi Matthew
Did you manage to test triangulation with the beacons ?
Matthew says
Unfortunately not yet. I need to get it finished and updated here.
Ippo says
Thanks a lot for this tutorial, very usefull !
However, i would like to receive notification when the app is not running.
I have set in the initRegion :
self.beaconRegion.notifyEntryStateOnDisplay = YES;
but i’m still not receiving notification when the app is not lunched.
Can you help me please :)
MatthewN says
I haven’t looked in to this too much, but perhaps my latest tutorial posted a few minutes ago might have the answer.
Link: https://www.devfright.com/ios-7-background-app-refresh-tutorial/
Scroll right to the end of that tutorial and the last bit of sample code is for AppDelegate.m. On lines 53 – 61 you see how to send notifications on your device.
I would suggest modifying this to suit your needs and then put it in the didEnterRegion method in the TrackViewController.m (implementation). I haven’t tested if this will work, but I’m pretty sure that this method works in the background and that you should be notified with whatever message you want when entering the beacon region.
Back to the code found in the AppDelegate on that latest tutorial, for alertBody you could also use the class method stringWithFormat and pull in something like @”Welcome to the %@ room at the %@ Museum” and then pull in the required information.
Ippo says
Thank you for your answer,
and sorry for my late answer (i was working on another project)
I’m going to try that this week,
and i’ll keep you posted if it works or not ;)
Thanks !
FilipeM says
Hello Ippo,
did you get this to work ? I’m having the same question and for my current understanding this is not possible. Do you have any other information ?
Thanks
Filipe
“I read your other article about the notifications and was hoping that even after the user terminate the app (terminate = close via app launcher and app no longer available in springboard) it would be possible keep monitoring for regions (providing the user had given authorization ) and send a simple notification on region crossing.
Unfortunately, for my understanding and tests, this is not possible by design it only works when app is running in foreground or background (thus is available in springboard).
Is this true or am i missing something?”
Ippo says
Hi FilipeM,
Unfortunately, it doesn’t work for me too.
I have asked the company Estimote it that was possible with their SDK
and they told me :
” We have been encountering the same issue and we are confident that this is an issue on Apple’s side. We have to file a bug report with Apple.”
Can you try to ask Apple on iOS dev center ?
Best Regards
DuncanChampney says
See my previous post. iOS 7 handles apps that have been launched before differently than iOS 6 did. Apps that were terminated because of memory pressure are still listed in the task list that comes up when you double-tap the home button, and an app that was terminated by the system due to memory pressure will be re-launched if it registered for beacon notifications.
You need to train your users not to terminate your apps if you want to receive beacon notifications.
DuncanChampney says
Ok, here’s how it works.
In order to receive beacon notifications, the user has to have launched your app and given it permission to do so. Then if you use the location manager to start monitoring for beacon regions, you will get notified even if your app is in the background, or even if your app is terminated due to memory pressure. (The system takes steps to take over region monitoring on your behalf and re-launch you if it detects a beacon event for a region you’ve registered interest in.) Now, if the user actively terminates your app, or has never launched it/given it permission to monitor for beacon regions, then the system will not monitor beacons on your behalf.
Does that help?
Tardigrade says
So is it correctly understood that there’s no static information telling for what UUID etc the app should launch, but rather the app has to run at least once to set that up?
MatthewN says
Correct. Your app is responsible for specifying what UUID to monitor for and as long as you run it once, it will be monitoring for those iBeacons with that specific UUID (I believe you can monitor up to 20 UUID’s at once).
Mehul says
This tutorial is really awesome but i dont get onething. As you have told that we can send audio as well as video after device comes nearer to Beacon, can you explain how to do that.
MatthewN says
I’m not aware of being able to transmit with iBeacons anything other than the few listed items such as the UUID. To work with audio/video, you would have that stored online and when you hit a beacon with certain criteria it would allow the app to know which content it should fetch. Moving from room to room would trigger different content to be fetched.
Haim Carmel says
Thank you for a great tutorial !!!
Adub says
If two devices were to communicate with each other, would there be a way to pass some sort of string property between the two? For example a device detects another device and it is able to register it as “Billy’s phone”.
MatthewN says
Not that I’m aware of… but, you could use some service such as Parse.com and have Billy register his UUID and then have the other device subscribe to Billy. When it comes in to contact with Billy’s UUID it would notify you.
Adub says
But doesn’t the app need to be configured so that if two devices were wanting to talk to each other they would need the same UUID?
MatthewN says
Correct. If you want to “track” Billy, you would go to the database where he registered his UUID and you would take that UUID and add it to your side and instruct the receiver to look out for that UUID.
DuncanChampney says
Not with iBeacons. However, you can use iBeacon for device discovery and then open a “regular” BLE 2-way communication between the devices.
Alfo Tech says
Thanks MatthewN for great tutorial . I wonder is it possible to use this feature to detect motion with only one device . what i would be interested to do is to use my iPhone 5 , install this app my phone. now if I cover one or more meters distance within same place , i need a way to get notified that my location is changed ( with assumption that i am carrying my iPhone 5 with me of course ) No other info is needed at this stage. can we do some trick to do this ?
Regards
Alfo
MatthewN says
Do you mean literally just 1 device (ie, either no transmitter or no receiver)? If so then iBeacons will not work. You need to have a transmitter and a receiver. Another option for you would be to use GPS (the setting with the highest accuracy) or perhaps come up with some smart way to detect movement with the accelerometers and gyroscope.
Eric McGary says
Have you actually been able to get the didEnterRegion delegate method to call? I see in your Github project that you call the didEnterRegion delegate method your self.
MatthewN says
Yes. If you remove that manual call and then walk away from the transmitter out of range and then wait for something like 20 seconds before you re-enter the region. I can’t remember the exact time you need to wait but I think it is around the 20 second mark. If you re-enter before that point then iOS ignores that you entered the region.
Abhi says
thanks for this tutorial,
I was also trying to send additional information using CBMutableCharacteristic and CBMutableService classes can i do that?
MatthewN says
I’ll need to investigate that. Let me look in to it and get back to you.
Yves Schleich says
Thanks. Very good explanation.
disqus_B6wwKiX6vk says
Hi guys, excellent material! Thank you!
If it’s not asking too much, could you also show us how to display alerts on the locked-screen or “revive” the app when the IOS7 device comes in range of a recognizable beacon? I’ve read somewhere that IOS7 can “recognize” the beacon being advertised and trigger the appropriate app even if that app is not running in the background anymore. That would be a great help and enhancement of your tutorial!
Thank you in advance!
Your site rocks!
Flavio
MatthewN says
Hello Flavio,
If you check out this tutorial:
https://www.devfright.com/ios-7-background-app-refresh-tutorial/
It contains the code that you need to use local notifications. If you scroll to the bottom of the article to the code for AppDelegate.m and look at line 59 onwards, it contains the code needed to show alerts.
To get this working for detecting iBeacons, put it in the didEnterRegion delegate method. When you enter the region of an iBeacon, this code would get executed and would provide a notification. You would need to change the alertBody to reflect what you were doing such as “You entered %@ room” and you could assign an NSString to that value and depending on which iBeacon was found, you would put whatever text was needed such as “You entered the living room”.
Hope that helps, but if not ask again and I’ll look in to it further.
DuncanChampney says
Apple’s sample app AirLocate shows how to do this. It’s pretty trivial, really. I’ve developed my own test app that does the same thing, using AirLocate as a model.
Harrison Lee says
Hi Matthew,
You can replace the didStartMonitoring for region workaround by calling the location manager’s requestStateForRegion method and adding something like this for the delegate call back:
– (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region {
// If we wake up and find we’re already in a region, start ranging
if ([region isEqual:self.beaconRegion] && state == CLRegionStateInside) {
[_locationManager startRangingBeaconsInRegion:(CLBeaconRegion *)region];
}
}
MatthewN says
Very good idea! Thanks for the tip Harrison.
Wayne Strobel says
Hi –
I’m not a programmer, but an entrepreneur who is needing to develop a simple App for ibeacons.
Basically I need it to do what is described in your Museum example… but even simpler. Forget that there are different rooms in each museum. The museum simply wants one iBeacon every few miles (so there’s no need to address multiple beacons within range at the same time). Furthermore, the museum simply wants the App to launch a web page when it is within range of the beacon.
Another way to look at this is that you just want it to perform a task like an NFC chip, and launch a URL.
Assuming I have a beacon transmitting a UUID, Major, and Minor… my understanding is that the App is awakened by the iPhone when the phone notices a beacon advertising a specific UUID. At that point, the App uses the Major and Minor information to decide what it needs to do.
If so, then the App uses the Major and Minor to determine the URL (possibly from a lookup table) and provides a notification to the user asking them if they want to launch the web page.
Can someone please tell me if I have a clear understanding of the basic concept? And if not… clear up my misunderstanding.
Finally, assuming I’m not too far off the mark… is this a relatively easy App to develop? How much could I expect a reasonable App developer to charge for prototyping such an App?
Thanks in advance for your help. I’ve spent hours reading over lots of information in your site, but in the end decided I can’t spend the time to learn to develop the App myself. I need someone to do it for me.
Thanks again.
MatthewN says
You are correct in your understanding. Your app will have a specific UUID and so will the transmitters. If these match, you can query the major/minor values (or just one of them it seems for this app as none others will be in range at the same time). If it = 1 (or whatever) then show this and if 2, show that and so on.
The app doesn’t sound complicated, drop me an email if you like ([email protected]) and I’ll share my thoughts on pricing etc…
John Nguyen says
“my understanding is that the App is awakened by the iPhone when the phone notices a beacon advertising a specific UUID”
Is this correct? Or user has to run the app before it can detect the beacons with specific UUID?
MatthewN says
Sorry, I missed this question.
As long as you have opened the app at least once after installing (and not swiped it out of multitasking) then beacons can be detected in the background and your app can wake up for a short period of time (around 30 seconds if I remember correctly).
Karsten Reuter says
Did anybody try or manage to create a pass for passbook with Beacon info? I would be interested in some guidance.
MatthewN says
I haven’t tested but would like to try this. I’ll see what I can find.
mabco says
Nice simple tutorial. Thanks. One thing, I’m pretty sure UUIDString on NSUUID is not a property but just an instance method.
MatthewN says
I believe you are correct. There’s a few references to various UUID’s and that one is an instance method while others are properties etc…
FilipeM says
Great article.
I read your other article about the notifications and was hoping that even after the user terminate the app (terminate = close via app launcher and app no longer available in springboard) it would be possible keep monitoring for regions (providing the user had given authorization ) and send a simple notification on region crossing.
Unfortunately, for my understanding and tests, this is not possible by design it only works when app is running in foreground or background (thus is available in springboard).
Is this true or am i missing something?
MatthewN says
I see the same happen with OmniFocus. If I kill the app it stops monitoring for regions. I don’t think it did this in the past. I’m not sure if this is by design or not unfortunately.
Michael McGuire says
Yes, if an app is terminated, it will no longer receive any region notifications, iBeacon or GPS.
MatthewN says
After watching that 2013 WWDC video, I seem to recall it mentioned that since iOS 7 they decided to honour what the user wanted. If they wanted to terminate the app in app switcher, then it switched everything off completely for that app.
zappit says
Hi all. So we’re having real trouble with RSSI. Our App says ‘RSSI: 0’ – any help HUGELY appreciated???
Matthew says
Try generate a new UUID at the terminal on your Mac.
The command is…
uuidgen
Copy and paste the new UUID in to the transmitter code and receiver code. I found helping someone else out that they also had a similar issue and it turns out that UUIDs cannot be random but need to be generated by that command above or in code.
MatthewN says
I suggest generating a new UUID with the command at the terminal:
uuidgen
This has solved the issue for myself and someone else here in the past… let me know if not.
Joe Wong says
Hi Matthew, great tutorial! One question: what if two beacons with the same UUID are in close proximity? Will both be discovered by a backgrounded app or will one of them be ignored? Does it make any difference if the beacons have different major values? Thanks!
MatthewN says
Hello Joe. All beacons need to have the same UUID so that a particular app can pick them up. The major and minor values can be used to distinguish between X amount of beacons within a company. If you are in proximity to two or more beacons at the same time.
-(void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region {
The beacons NSArray contains all the beacons currently in range although in the example above, I only opted to select the lastObject which means only 1 beacon is used in this app. You could change the code to look at the whole NSArray to see if more than 1 CLRegion is in that NSArray and if so, grab the info from each one.
Joe Wong says
Thanks a lot! If the app is running in the background, will I be informed of region entries and exits for each beacon even though they are very close to each other?
MatthewN says
I believe so.
Joe says
Why did you initialize the beaconpointer before set the pointer to [beacons lastObject]; on:
-(void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region {
CLBeacon *beacon = [[CLBeacon alloc] init];
beacon = [beacons lastObject];
? Would it be ok, when i write:
-(void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region {
CLBeacon *beacon = [beacons lastObject];
?
Top Sample, Thanks a lot
MatthewN says
Hello Joe,
You are correct. You can omit the CLBeacon *beacon = [[CLBeacon alloc] init]; from the code and it will work just as needed.
Tardigrade says
Even if the app is shut down and/or the phone is restarted?
Meseery says
Hello there,
A great tutorial i consider to start on ,i only have some investigations i hope people here share with me ,consider this case :
i have a list of UUIDs (ex. iPhones ),theses devices are used via one applications to receive some data.
when one of those UUIDs come close to a beacon, it fires a notification of some data sent to this UUID, so how could i achieve that ?
Thanks
MatthewN says
I’m not sure I fully understand so feel free to post more details here or if you prefer to keep your idea off of a blog then please email [email protected] if you need a more specific answer.
One problem I see with the above is that the UUID of the beacon and the phone need to match and as far as I am aware, all iBeacon transmitters can only have 1 UUID (although you can monitor up to 20 different UUID’s from the same app from what I understand).
Also the iBeacon isn’t really smart. It only has a UUID, major and minor value. So, each phone would detect it the same. Any logic regarding messages would need to be done on the phone rather than the beacon.
Please do email if I misunderstood your question :)
Meseery says
To be more specific, i have some beacons say 20 iphones with 20 UUIDs and 1 beacon at a shelf in some store,how could i notify the iphone of a specific message or notification saying like “you ‘re in the region of beacon 1” .. that’s what i want ,but consider also the case of multiple beacons and multiple iphones.
Thanks
MatthewN says
You do that with the major/minor value. All of your beacons in the store will transmit the same UUID but a different major/minor value. If you have several beacons at one location you might opt to go for the same major value but different minor values.
All those values will either be coded in to the app so that it knows what message to share, or it could be connected up to a web service where each time an iPhone goes in to a beacon region, it looks up online a database showing major/minor values and then shows whatever message is set server side.
Meseery says
Well, could you guide me to which part in the tutorial that tell me when the iphone goes in the region of a specific beacon, supposed it’s one beacon.
MatthewN says
This is the delegate method called when the device enters a region of a beacon.
– (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region {
NSLog(@”Beacon Found”);
[self.locationManager startRangingBeaconsInRegion:self.beaconRegion];
}
Meseery says
Alright,i did use this tutorial in my app and i am pleased of it,great work, but actually i had a problem i think it should be stunning, the CLBeacon class when i detect iphone device nearby doesn’t provide me with any data relative to its iphone like actuall UUID, storage size,.. etc .. and this is what i stopped by right now !
MatthewN says
Hello Meseery,
The CLBeacon class provides the following 6 properties:
proximityUUID
major
minor
proximity
accuracy
rssi
The intention isn’t for CLBeacon to share any data about the device other than it has found a transmitter with a matching UUID, a major/minor value as well as a proximity, accuracy and rssi (signal strength). It’s just a better way of adding context to locations.
To try explain a little better what the idea is behind the iBeacon technology, think of geofences used in Core Location. Apple allows you to set up to 20 geofences (regions) that you can monitor entrance and exits of. When you enter a location such as a local shop, you get an alert which might remind you that you need to buy a particular item. But, Core Location limits that to just 20 geofences per app. There’s work arounds so you can get more, but officially its 20.
iBeacons takes this further and enhances it. You can monitor almost any amount of beacons dotted anywhere in the world and distinguish them with major/minor values (each of which can be 1 – 65535… a lot of combinations). Although you can only monitor for 20 different UUIDs at once (you likely only monitor for 1 UUID in your app). Being able to use iBeacons is like monitoring for 50, 100, 500, 1000, 10000, 50000 etc… geofences. Large companies like WalMart could kit out every store in the US with iBeacons and you have tens of thousands of regions that are monitored by your app. By each one having a different combination of major/minor values, the app can know exactly where you are “ie, You are in the DVD aisle in Walmart in Brigham City, Utah… there’s a special offer on at the moment… buy one get one free”.
I hope that helps clear this up a little.
goodgoodgood says
Hi Matthew, thanks a lot for the tutorial and demo code.
Sorry for asking this question one more time here, but the answers I’ve seen, so far, still keep me wondering about how notification works in background. Would you please tell me if I’m doing something wrong or if it doesn’t work the way I think?
1. I added “self.beaconRegion.notifyEntryStateOnDisplay = YES;” to didEnterRegion
2. I also added a localNotification saying “Welcome” to it
3. I have an iPad with the app on foreground transmitting beacons
4. I have the app on my iPhone, I selected “track beacons”
5. I can receive the beacons within it (iPhone)
6. I close the iPhone app
7. I go far outside the room and get back after 30 seconds or more
But I don’t receive any kind of notification telling that I entered a beacon region? Was it supposed to work or the app will never receive notifications once it is closed and not on background?
Thank you for any help or explanation.
MatthewN says
Can you keep your phone attached to the Mac and run the app from Xcode and NSLog on the didEnterRegion something like NSLog(@”Beacon detected”);
Then walk out of the room with the transmitter and at least out of range for 20 – 30 seconds and see if you have an event NSLogged to the screen when you get back.
That will help determine if the didEnterRegion is being fired.
If not, check that you have self.locationManager.delegate = self; somewhere in your code and that you show in the header that you conform to the location manager delegate protocol. It could be that the method isn’t being triggered.
goodgoodgood says
Thanks a lot for your reply! Yes, I did exactly what you said, it works perfectly if the app is running on both foreground or background, but nothing is triggered if the app is not running. So I wonder if it is possible for the app to wake up with background region monitoring, just like is possible with normal CLLocation “significantChanges”, but using an iBeacon region in this case.
Michael McGuire says
The documentation is confusing, but I believe you want to set notifyEntryStateOnDisplay to NO, not YES. If set to YES, I believe you only get the notification when the user has the display on and the device is already in the region. Make sure to watch the “What’s New In Core Location” WWDC 2013 video for more information. I think they explicitly mention this flag.
goodgoodgood says
Yes, they do mention. What I understand is that users are only notified when they turn on the device/screen, but still, a bit confusing to me. I cannot get any enter region notification when the app is totally closed.
Andy Delarge says
You guys resolved that issue ? I`m trying to figure it out , but I`m in trouble here …
MatthewN says
Yes. I believe the issue was that the app was swiped out of the recently used apps multi tasking section. When this happens, the app does what the user wants and stops being updated in the background. It didn’t work like this in the past. in iOS 6, if a user killed off an app that way, region monitoring still worked. But now, it doesn’t in iOS 7.
So, to get the notifications or indeed, process while in the background, the user needs to just close the app out rather than swipe it out and kill it completely.
MatthewN says
Here is the explanation from Apple:
“If you force an app to quit by dragging it up from the multitasking display, it won’t be able to do its background activities, such as tracking location or responding to VoIP calls, until you relaunch the app”
http://support.apple.com/kb/ht4211
MatthewN says
Do you also want to drop me an email and I’ll send some sample code of what works for me?
goodgoodgood says
Thank you. I sent it.
Meseery says
Hi matthewn, i had applied this recipe but didEnterRegion: method never called, i also added NSLog message when the beacon enter the region and never logged out ,so i don’t know how but it happened, didExitRegion is called once i transmit the beacon ! .. any explanation why ?
Hugh Mbaezue says
Hello
Thank you very much for your amazing tutorial on iBeacons. I found it very helpful for my next up coming project.
I have a question which I have not been able to figure out so I decided to contact you for help. I have been able to understand the basic function of iBeacons. Setting up UUID’S and major and minor id’s to specify exact notifications, but my question is how do I dynamically update information I send out to the users without having to go into the code each time to do this. Do I need to create a database to store al my information I want to push out to users? if so how will this database constantly refresh messages pushed out to users? An example would be lets say if you walk into a store and you get a notification in the shoe section saying there is a 10 percent off, you look at the notification but not too impressed and start to walk out, then you get another notification saying for today only you can get a 25 percent off… The app has to dynamically refresh for this to be possible.
Please help me clarify this
MatthewN says
It sounds like you just need some good logic adding to the app to set various things such as “when entering beacon region, send notification” and “when leaving shop, send this notification”. The logic will need to handle what happens if the user opts for the 20% option as they might get upset if they walk out of the store and then get informed they could of had 25% off the same product.
So, a bit of collaboration between database, beacons and user interaction will be needed.
Sorry the answer isn’t too simple, but it’s a complex issue to resolve that needs some planning on how to best implement it.
Hugh Mbaezue says
Yea sound pretty complex. I was just reading your other tutorial on background refresh and was wondering if there is a way to manually change values in the database and have the app fetch the values from the database without doing a pull to refresh. Like manually go into the database, change values and automatically the iBeacon app reads the new values and displays it to the user using a background refresh feature?
MatthewN says
These types of tasks can be run in the background (assuming the process doesn’t take too long… I think you have 20 – 30 seconds to complete a task in the background.
Alternatively, you could just run the code along side your local notification when you enter a region. As soon as you enter the beacon region, send the notification and then perform the DB work in that didEnterRegion method.
Hugh Mbaezue says
I was able to use parse.com to store data directly onto their servers but I can’t seem to retrieve that data to the beacon app I am developing. I tried configuring my app to look to the online servers and I exported the uuid to the online servers and typed in a couple of values but the online servers fail to send notifications I specified when the user enters the transmitted region.
Is it possible I send you the source code I am using so you can better understand what I am trying to do? I feel you can get a better understanding once you look at my source code
AdamG says
Hi Matt,
This is a great tutorial.
Do you know if two apps can broadcast as a beacon on the same phone at the same time? Or does the second app that loads cancel the first app? Or can an app not broadcast while in the background?
Thanks.
MatthewN says
At the moment I haven’t been able to get the device transmitting while the app is in the background, so this is something I need to test.
If that works, I’ll be able to tell you if the iPhone can transmit 2 beacons at the same time from different apps.
AdamG says
Thanks for the quick reply.
I found this line in the startAdvertising description:
Data advertising is done on a best effort basis, because space is limited and there may be multiple apps advertising simultaneously.
https://developer.apple.com/library/ios/documentation/CoreBluetooth/Reference/CBPeripheralManager_Class/Reference/CBPeripheralManager.html#//apple_ref/doc/uid/TP40013015-CH1-SW9
Sounds like it’s possible, but it will take me a while to test it too.
MatthewN says
I’ll see if I have time tonight to test and see what I can come up with. Perhaps a background mode needs enabling?
CrownOfThornz says
How do I get the beacon to stop transmitting? Lets say I no longer want the beacon to transmit, whenever I pop the view back to the main view controller.
MatthewN says
The clumsy way is to just hit the home button and close the app. The beacon stops transmitting and the receive will call the didExitRegion method about 5 – 20 seconds later depending on if you are ranging beacons or not. Sometimes it takes a bit longer.
To stop transmitting with code, connect up a UIButton with IBAction and add this code:
[self.peripheralManager stopAdvertising];
CrownOfThornz says
Does stopAdvertising mean its no longer looking for a beacon or its no longer transmitting? I added that line of code and it didn’t do anything.
MatthewN says
That line is for the transmitter side that will run on a second iOS device and transmit the beacon.
CrownOfThornz says
Thanks for the quick reply Matt. I got it working. Thanks!
ashley505 says
Hello great tutorial!
I have a question. How do I test for other iBeacons? lets say i have multiple iBeacons with the same UUID and Major id’s but different Minor id’s? Your tutorial only explains how to test for one beacon, but I will love to know how I can test for multiple beacons at once.
Could you please help me out with testing for multiple beacons?
MatthewN says
The CLBeacon objects in the didRangeBeacons are presented in an NSArray which has all CLBeacons in range.
In the tutorial we just used:
beacon = [beacons lastObject];
Instead of that, you could put a for loop around it with a counter and use something like:
beacon = [beacons objectAtIndex:i];
Every time the didRangeBeacons delegate method is called (once a second when ranging), this will pick up all beacons for you to do what you need with them.
ashley505 says
Thank you for the response. Did you mean “beacon = [beacons objectAtIndex:1];” ? Cus I tried “beacon = [beacons objectAtIndex:i];” and got an error..The error says “Use of undeclared variable”
MatthewN says
I meant the one with i like I mentioned. It sounds like you didn’t put it in a for loop like suggested…
for (int i = 0; i < beacons.count; i++) {
beacon = [beacons objectAtIndex:i];
// Your code that handles what you need to do with the beacons.
}
Marc says
Guys, we’re looking for a freelancer who wants to earn some money helping us program beacons. Interested? Drop my a mail at [email protected]
Myers AppDevelopment says
Can you setup a device to act as a beacon as well as search for the signal from other beacons simultaneously? I would like to make 2 separate phones beacons that still connect to each other.
davidgeek says
You cant do that with Bluetooth LE, they must act as hub or beacon but not both at the same time, but you will be able to in the next version, bluetooth 4.1.
ææ¦ å´ says
HI-
I want to obtain the accurately distance between the iBeacon and the divece.
Can someone help me ?
thanks a lot!
Matthew says
What have you tried so far that has worked?
CrocodilesPoor says
Why did you call “[self locationManager:self.locationManager didStartMonitoringForRegion:self.beaconRegion]”
That delegate method will be called automatically after the call to startMonitoringForRegion. You’re not supposed to call it.
Now it’s going to call twice.
Matthew says
As mentioned in the example, it was just a shoddy work around for when you are already in a region and not passing a boundary. The way it should be done is with the locationManager:didDetermineState:forRegion: method.
Christopher Drum says
A small thing, but a thing. NSNumber has a stringValue method; so, [beacon.major stringValue] can be used, rather than the long-way-around [NSString stringWithFormat…]
Matthew says
Thanks Christopher. I think the stringValue way around is the cleaner way to do it (one I use in my code recently).
Tony Knows says
HI !
This is great – thanks – I just got some beacons from china how do i set them up ?
David Leconte says
hi tony did you get this working with your beacons? i´m trying and i could´t
Matthew says
All of the beacons I have are set up in different ways. Each manufacturer provides details or apps that can be used to update UUID, major/minor and various other information for the iBeacon. What beacons did you get?
Priyanka V.K says
Hi Mathew,
The tutorial was of great help,but i would really appreciate if you could help me in the following scenario – am running some other app,beacon app is in background,beacon app should launch and come to front when a transmitter has been detected.
Matthew says
Perhaps in the didEnterRegion delegate method, make use of the uilocalnotification class and send a local notification (will slide down at the top as a banner) which they can tap on.
MatthewN says
You can use the didEnterRegion delegate method from CLLocationManagerDelegate. When you enter the region, this is called and then you start ranging the beacons. In the background you just have a few seconds to do the work.
In didRangeBeacons (delegate method), put your code in there to trigger the UILocalNotification. This will then put an alert at the top of the screen when using other apps, or on the lock screen if the app is closed.
The accuracy and triggering of the didEnterRegion isn’t always 100% accurate but it works in most cases.
Sung says
Hello!! Thanks for the great tutorial!!!
I’m working on my Exhibition app using iBeacon technology.
I figured out ibeacon parts but I’m stuck in presenting specific view in my UIscollview.
I set up my content size and every view has information of artwork.
This is my didRangeBeacon method
CLBeacon *beacon = [[CLBeacon alloc] init];
beacon = [beacons firstObject];
if (beacon.proximity == CLProximityImmediate) {
[self showAlbumInfoWithUUID:beacon.proximityUUID
major:beacon.major
minor:beacon.minor];
}
I have no idea how should i implement showAlbumInfoWithUUID: major: minor: method…..
This method should change its view automatically according to beacon’s uuid, major and minor value.
Matthew says
In this method:
showAlbumInfoWithUUID:beacon.proximityUUID major:beacon.major minor:beacon.minor
you would need to add some logic in either kept locally within the code, stored locally in core data or sitting on a web server somewhere. If major/minor matches a certain value then show 1 thing and if something else show something else. It’s hard to explain without writing out a bunch of code, but you need to break down your app in to bite sized chunks and store the content to be displayed somewhere (on a web server, in the app etc…) and figure out how to reference that data and then pass the major/minor values so that the pair can bring up some content.
RLSchmidt says
Hello! Thanks for the great tutorial. I’m a newbie to IOS development. I’m having trouble importing CoreBluetooth Framework. I’ve added (+) under General->Linked Frameworks and Libraries. The Framework shows up in the list of Frameworks in my project view, but the build fails at:
#import error: ‘CoreBluetooth/CoreBluetooth.h’ file not found
No problems with CoreLocation Framework which I import in the same way. Any ideas why CoreBluetooth Framework would be giving me grief? Have I missed a step?
Thanks!
RLSchmidt says
Found the problem… I had to clear the Framework Search Paths in build settings. The app builds now!
Matthew says
Well done :)
Hemant Sharma says
I have a peculiar situation… i installed this app on my 2 iOS devices. An iPhone 4s and iPhone 5.
The problem is that my iPhone 4s cannot Track when iphone5 is Transmitting. whereas the iPhone 5 can track when 4s is transmitting. I think my 4s doesn’t have a hardware issue because it can read/write normally using BLE on apps like PKPKT or other ble tutorials.
Matthew says
I use a 4S and can broadcast as a beacon or receive as a beacon. Are you sure you have everything matched up and that you are broadcasting the correct UUID etc…
Kamar Shad says
Hi,
firstly i like to say a great thanks to you writing for such nice and easily understandable tutorial :)
I am thinking to a develop an iOS application which will interact with iBeacon technology enabled devices.
Before starting to work want to clear basic concept of it, I have following questions:
1)UUID,Major,Minor: is this mandatory to keep hardcoded or in .plist data structure to make call with beacon devices.
2)does the beacon device send all these information itself to near about iOS application(this might be the silly question).if not this is not possible,can i keep all the monitoring beacons devices info (UIID,major,minor)on my server and fetch these information from server then start monitoring these fetched Beacons.
3)Display Notification:is this responsibility of iOS beacons api to send notification when any ios application comes into the region of any beacons device.
Please clear my above doubts, will be huge thanks to you.
Regards,
Kamarshad
Matthew says
1. You can keep a database of UUID, major/minor wherever you feel works best. I normally have them online and request the data when the beacon comes in to range.
2. An iBeacon constantly transmits a signal. If you have an app configured to listen out for a particular UUID and there’s a match, it will call a delegate and you can then use ranging to get the major/minor value of that beacon and query a DB (if you wish) to find out what to do with that beacon.
3. Notifications are not integrated through iBeacons. Typically you would come in to range of a beacon and then fire a UILocalNotification if desired.
Shubhank Gaur says
Is the didRangeBeacons method supposed to invoke each second in a loop when a beacon hardware is in range ?
If so, whats the best way to call a custom method (to say hello or send a pUSH) when a beacon comes in range without the method itself looping, which won’t be possible via didRange method I assume ?
didEnter method won’t work as well as I need the beacon’s UUID to compare it to know which PUSH method to invoke
Matthew says
Yes, the didRangeBeacons delegate runs once per second and provides information about the current beacons in range, if any.
To get the actual beacon data you need to use didRangeBeacons. If you are OK with sending a notification based on just the UUID then didEnterRegion will suffice. That is only called once when a beacon comes in to range and won’t be called again until the region exit occurs.
If you do need beacon details, startRangingBeacons on the entry to the region and stopRangingBeacons when you have the information you need.
Waqas says
Hello Methew its really a helpful tutorial
Now what i want that to show a welcome and good by message and want to run app on background
thanks in advance
Nipun says
Hi Matthew,
Thanks for sharing this nice tutorial.
I have few stcknfind beacons and I’ve configured those into iBeacons successfully using the stcknfind sdk, and in my app I’m trying to fetch the beacons and I’m getting the beacons in -(void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region, however beacon proximity is always “CLProximityUnknown”, can you please suggest something here, I’ll really appreciate that.
Cheers
Nipun
Matthew says
What happens if you NSLog the rssi property? Does it provide a value which increases until you get as close as you can get? You should first start seeing beacons around the -90 RSSI and it will increase to about -30 at its closest. If that works, then maybe you could use those values such as “-30 to -45 is immediate”.
Mike Post says
Given that this is quite an old blog post now, in terms of iOS releases, it’s understandable that anyone who’s trying to get it working for iOS 8 will be scratching their heads.
For beacons to work in iOS 8, you need to enable the NSLocationWhenInUseUsageDescription key in the plist (the value will be a description of your choice).
Then in the TrackViewController’s viewDidLoad method, add this line:
[self.locationManager requestWhenInUseAuthorization];
Gustavo says
Hi Matthew,
Thanks for sharing this nice tutorial.
I have a question for you. Do you know if the Apple Watch can act like a ibeacon?
Thanks al lot!!!
Matthew says
I am not sure on that one. It certainly has bluetooth, but I don’t believe it can be used for anything other than communication with an iPhone so that the two devices can stay connected.
Khushi says
Hi
I am using test code example given here and testing it on 2 devices (iPhone 4s & iPhone 5). didRangeBeacons, didEnterRegion or didExitRegion delegates are not getting called.that’s why i am not able to detect beacon. I have made iPhone5 as a transmitter and other device as a receiver. i am checking in foreground.
Can any one help what i am missing here.
Khushi says
its working :) Thanks
Khushi says
I have one query. If any one has any idea please share., Can we detect iBeacon in an audio aid.??
satya murty says
i am trying to run this application. but i got this issue. “[CoreBluetooth] XPC connection invalid
can any body please help me
“