In this article, by Craig Gilchrist, author of the book Learning iBeacon, we’re going to expand our knowledge and get an in-depth understanding of the broadcasting triplet, and we’ll expand on some of the important classes within the Core Location framework.
(For more resources related to this topic, see here.)
To help demonstrate the more in-depth concepts, we’ll build an app that shows different advertisements depending on the major and minor values of the beacon that it detects. We’ll be using the context of an imaginary department store called Matey’s. Matey’s are currently undergoing iBeacon trials in their flagship London store and at the moment are giving offers on their different-themed restaurants and also on their ladies clothing to users who are using their branded app.
Uses of the UUID/major/minor broadcasting triplet
In the last article, we covered the reasons behind the broadcasting triplet; we’re going to use the triplet with a more realistic scenario. Let’s go over the three values again in some more detail.
UUID – Universally Unique Identifier
The UUID is meant to be unique to your app. It can be spoofed, but generally, your app would be the only app looking for that UUID.
The UUID identifies a region, which is the maximum broadcast range of a beacon from its center point. Think of a region as a circle of broadcast with the beacon in the middle.
If lots of beacons with the same UUID have overlapping broadcasting ranges, then the region is represented by the broadcasting range of all the beacons combined as shown in the following figure. The combined range of all the beacons with the same UUID becomes the region.
Broadcast range
More specifically, the region is represented by an instance of the CLBeaconRegion class, which we’ll cover in more detail later in this article. The following code shows how to configure CLBeaconRegion:
NSString * uuidString = @"78BC6634-A424-4E05-A2AE-A59A25CAC4A9"; NSUUID * regionUUID; regionUUID = [[NSUUID alloc] initWithUUIDString:uuidString"]; CLBeaconRegion * region; region = [[CLBeaconRegion alloc] initWithProximityUUID: regionUUID identifier:@"My Region"];
Generally, most apps will be monitoring only for one region. This is normally sufficient since the major and minor values are 16-bit unsigned integers, which means that each value can be a number up to 65,535 giving 4,294,836,225 unique beacon combinations per UUID.
Since the major and minor values are used to represent a subsection of the use case, there may be a time when 65,535 combinations of a major value may not be enough and so, this would be the rare time that your app can monitor multiple regions with different UUIDs. Another more likely example is that your app has multiple use cases, which are more logically split by UUID.
An example where an app has multiple use cases would be a loyalty app that has offers for many different retailers when the app is within the vicinity of the retail stores. Here you can have a different UUID for every retailer.
Major
The major value further identifies your use case. The major value should separate your use case along logical categories. This could be sections in a shopping mall or exhibits in a museum. In our example, a use case of the major value represents the different types of service within a department store.
In some cases, you may wish to separate logical categories into more than one major value. This would only be if each category has more than 65,535 beacons.
Minor
The minor value ultimately identifies the beacon itself. If you consider the major value as the category, then the minor value is the beacon within that category.
Example of a use case
The example laid out in this article uses the following UUID/major/minor values to broadcast different adverts for Matey’s:
Department |
Food |
Women’s clothing |
|
UUID |
8F0C1DDC-11E5-4A07-8910-425941B072F9 |
||
Major |
1 |
2 |
|
Minor |
1 |
30 percent off on sushi at The Japanese Kitchen |
50 percent off on all ladies’ clothing |
|
2 |
Buy one get one free at Tucci’s Pizza |
N/A |
Understanding Core Location
The Core Location framework lets you determine the current location or heading associated with the device. The framework has been around since 2008 and was present in iOS 2.0. Up until the release of iOS 7, the framework was only used for geolocation based on GPS coordinates and so was suitable only for outdoor location.
The framework got a new set of classes and new methods were added to the existing classes to accommodate the beacon-based location functionality. Let’s explore a few of these classes in more detail.
The CLBeaconRegion class
Geo-fencing (geofencing) is a feature in a software program that uses the global positioning system (GPS) or radio frequency identification (RFID) to define geographical boundaries. A geofence is a virtual barrier.
The CLBeaconRegion class defines a geofenced boundary identified by a UUID and the collective range of all physical beacons with the same UUID. When a device matching the CLBeaconRegion UUID comes in range, the region triggers the delivery of an appropriate notification.
CLBeaconRegion inherits CLRegion, which also serves as the superclass of CLCircularRegion. The CLCircularRegion class defines the location and boundaries for a circular geographic region. You can use instances of this class to define geofences for a specific location, but it shouldn’t be confused with CLBeaconRegion. The CLCircularRegion class shares many of the same methods but is specifically related to a geographic location based on the GPS coordinates of the device. The following figure shows the CLRegion class and its descendants.
The CLRegion class hierarchy
The CLLocationManager class
The CLLocationManager class defines the interface for configuring the delivery of location-and heading-related events to your application. You use an instance of this class to establish the parameters that determine when location and heading events should be delivered and to start and stop the actual delivery of those events. You can also use a location manager object to retrieve the most recent location and heading data.
Creating a CLLocationManager class
The CLLocationManager class is used to track both geolocation and proximity based on beacons. To start tracking beacon regions using the CLLocationManager class, we need to do the following:
- Create an instance of CLLocationManager.
- Assign an object conforming to the CLLocationManagerDelegate protocol to the delegate property.
- Call the appropriate start method to begin the delivery of events.
All location- and heading-related updates are delivered to the associated delegate object, which is a custom object that you provide.
Defining a CLLocationManager class line by line
Consider the following steps to define a CLLocationManager class line by line:
- Every class that needs to be notified about CLLocationManager events needs to first import the Core Location framework (usually in the header file) as shown:
#import <CoreLocation/CoreLocation.h>
- Then, once the framework is imported, the class needs to declare itself as implementing the CLLocationManagerDelegate protocol like the following view controller does:
@interface MyViewController : UIViewController<CLLocationManagerDelegate>
- Next, you need to create an instance of CLLocationManager and set your class as the instance delegate of CLLocationManager as shown:
CLLocationManager * locationManager = [[CLLocationManager alloc] init]; locationManager.delegate = self;
- You then need a region for your location manager to work with:
// Create a unique ID to identify our region. NSUUID * regionId = [[NSUUID alloc] initWithUUIDString:@" AD32373E-9969-4889-9507-C89FCD44F94E"]; // Create a region to monitor. CLBeaconRegion * beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID: regionId identifier:@"My Region"];
- Finally, you need to call the appropriate start method using the beacon region. Each start method has a different purpose, which we’ll explain shortly:
// Start monitoring and ranging beacons. [locationManager startMonitoringForRegion:beaconRegion]; [locationManager startRangingBeaconsInRegion:beaconRegion];
- Once the class is imported, you need to implement the methods of the CLLocationManagerDelegate protocol.
Some of the most important delegate methods are explained shortly. This isn’t an exhaustive list of the methods, but it does include all of the important methods we’ll be using in this article.
locationManager:didEnterRegion
Whenever you enter a region that your location manager has been instructed to look for (by calling startRangingBeaconsInRegion), the locationManager:didEnterRegion delegate method is called. This method gives you an opportunity to do something with the region such as start monitoring for specific beacons, shown as follows:
-(void)locationManager:(CLLocationManager *) manager didEnterRegion:(CLRegion *)region { // Do something when we enter a region. }
locationManager:didExitRegion
Similarly, when you exit the region, the locationManager:didExitRegion delegate method is called. Here you can do things like stop monitoring for specific beacons, shown as follows:
-(void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region { // Do something when we exit a region. }
When testing your region monitoring code on a device, realize that region events may not happen immediately after a region boundary is crossed. To prevent spurious notifications, iOS does not deliver region notifications until certain threshold conditions are met. Specifically, the user’s location must cross the region boundary and move away from that boundary by a minimum distance and remain at that minimum distance for at least 20 seconds before the notifications are reported.
locationManager:didRangeBeacons:inRegion
The locationManager:didRangeBeacons:inRegion method is called whenever a beacon (or a number of beacons) change distance from the device. For now, it’s enough to know that each beacon that’s returned in this array has a property called proximity, which returns a CLProximity enum value (CLProximityUnknown, CLProximityFar, CLProximityNear, and CLProximityImmediate), shown as follows:
-(void)locationManager:(CLLocationManager *)manager
didRangeBeacons:(NSArray *)beacons inRegion: (CLBeaconRegion
*)region { // Do something with the array of beacons. }
locationManager:didChangeAuthorizationStatus
Finally, there’s one more delegate method to cover. Whenever the users grant or deny authorization to use their location, locationManager:didChangeAuthorizationStatus is called. This method is passed as a CLAuthorizationStatus enum (kCLAuthorizationStatusNotDetermined, kCLAuthorizationStatusRestricted, kCLAuthorizationStatusDenied, and kCLAuthorizationStatusAuthorized), shown as follows:
-(void)locationManager:(CLLocationManager *)manager
didChangeAuthorizationStatus:(CLAuthorizationStatus)status { // Do something with the array of beacons. }
Understanding iBeacon permissions
It’s important to understand that apps using the Core Location framework are essentially monitoring location, and therefore, they have to ask the user for their permission. The authorization status of a given application is managed by the system and determined by several factors. Applications must be explicitly authorized to use location services by the user, and the current location services must themselves be enabled for the system. A request for user authorization is displayed automatically when your application first attempts to use location services.
Requesting the location can be a fine balancing act. Asking for permission at a point in an app, when your user wouldn’t think it was relevant, makes it more likely that they will decline it. It makes more sense to tell the users why you’re requesting their location and why it benefits them before requesting it so as not to scare away your more squeamish users.
Building those kinds of information views isn’t covered in this book, but to demonstrate the way a user is asked for permission, our app should show an alert like this:
Requesting location permission
If your user taps Don’t Allow, then the location can’t be enabled through the app unless it’s deleted and reinstalled. The only way to allow location after denying it is through the settings.
Location permissions in iOS 8
Since iOS 8.0, additional steps are required to obtain location permissions. In order to request location in iOS 8.0, you must now provide a friendly message in the app’s plist by using the NSLocationAlwaysUsageDescription key, and also make a call to the CLLocationManager class’ requestAlwaysAuthorization method.
The NSLocationAlwaysUsageDescription key describes the reason the app accesses the user’s location information. Include this key when your app uses location services in a potentially nonobvious way while running in the foreground or the background.
There are two types of location permission requests as of iOS 8 as specified by the following plist keys:
- NSLocationWhenInUseUsageDescription: This plist key is required when you use the requestAlwaysAuthorization method of the CLLocationManager class to request authorization for location services. If this key is not present and you call the requestAlwaysAuthorization method, the system ignores your request and prevents your app from using location services.
- NSLocationAlwaysUsageDescription: This key is required when you use the requestWhenInUseAuthorization method of the CLLocationManager class to request authorization for location services. If the key is not present when you call the requestWhenInUseAuthorization method without including this key, the system ignores your request.
Since iBeacon requires location services in the background, we will only ever use the NSLocationAlwaysUsageDescription key with the call to the CLLocationManager class’ requestAlwaysAuthorization.
Enabling location after denying it
If a user denies enabling location services, you can follow the given steps to enable the service again on iOS 7:
- Open the iOS device settings and tap on Privacy.
- Go to the Location Services section.
- Turn location services on for your app by flicking the switch next to your app name.
When your device is running iOS 8, you need to follow these steps:
- Open the iOS device settings and tap on Privacy.
- Go to your app in the Settings menu.
- Tap on Privacy.
- Tap on Location Services.
- Set the Allow Location Access to Always.
Building the tutorial app
To demonstrate the knowledge gained in this article, we’re going to build an app for our imaginary department store Matey’s. Matey’s is trialing iBeacons with their app Matey’s offers. People with the app get special offers in store as we explained earlier.
For the app, we’re going to start a single view application containing two controllers. The first is the default view controller, which will act as our CLLocationManagerDelegate, the second is a view controller that will be shown modally and shows the details of the offer relating to the beacon we’ve come into proximity with.
The final thing to consider is that we’ll only show each offer once in a session and we can only show an offer if one isn’t showing. Shall we begin?
Creating the app
Let’s start by firing up Xcode and choosing a new single view application just as we did in the previous article. Choose these values for the new project:
- Product Name: Matey’s Offers
- Organization Name: Learning iBeacon
- Company Identifier: com.learning-iBeacon
- Class Prefix: LI
- Devices: iPhone
Your project should now contain your LIAppDelegate and LIViewController classes. We’re not going to touch the app delegate this time round, but we’ll need to add some code to the LIViewController class since this is where all of our CLLocationManager code will be running. For now though, let’s leave it to come back to later.
Adding CLOfferViewController
Our offer view controller will be used as a modal view controller to show the offer relating to the beacon that we come in contact with. Each of our offers is going to be represented with a different background color, a title, and an image to demonstrate the offer.
Be sure to download the code relating to this article and add the three images contained therein to your project by dragging the images from finder into the project navigator:
- ladiesclothing.jpg
- pizza.jpg
- sushi.jpg
Next, we need to create the view controller. Add a new file and be sure to choose the template Objective-c class from the iOS Cocoa Touch menu. When prompted, name this class LIOfferViewController and make it a subclass of UIViewController.
Setting location permission settings
We need to add our permission message to the applications so that when we request permission for the location, our dialog appears:
- Click on the project file in the project navigator to show the project settings.
- Click the Info tab of the Matey’s Offers target.
- Under the Custom iOS Target Properties dictionary, add the NSLocationAlwaysUsageDescription key with the value. This app needs your location to give you wonderful offers.
Adding some controls
The offer view controller needs two controls to show the offer the view is representing, an image view and a label. Consider the following steps to add some controls to the view controller:
- Open the LIOfferViewController.h file and add the following properties to the header:
@property (nonatomic, strong) UILabel * offerLabel; @property (nonatomic, strong) UIImageView * offerImageView;
- Now, we need to create them. Open the LIOfferViewController.m file and first, let’s synthesize the controls. Add the following code just below the @implementation LIOfferViewController line:
@synthesize offerLabel; @synthesize offerImageView;
- We’ve declared the controls; now, we need to actually create them. Within the viewDidLoad method, we need to create the label and image view. We don’t need to set the actual values or images of our controls. This will be done by LIViewController when it encounters a beacon.
- Create the label by adding the following code below the call to [super viewDidLoad]. This will instantiate the label making it 300 points wide and appear 10 points from the left and top:
UILabel * label = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, 300, 100)];
- Now, we need to set some properties to style the label. We want our label to be center aligned, white in color, and with bold text. We also want it to auto wrap when it’s too wide to fit the 300 point width. Add the following code:
label setTextAlignment:NSTextAlignmentCenter]; [label setTextColor:[UIColor whiteColor]]; [label setFont:[UIFont boldSystemFontOfSize:22.f]]; label.numberOfLines = 0; // Allow the label to auto wrap.
- Now, we need to add our new label to the view and assign it to our property:
[self.view addSubview:label]; self.offerLabel = label;
- Next, we need to create an image. Our image needs a nice border; so to do this, we need to add the QuartzCore framework. Add the QuartzCore framework like we did with CoreLocation in the previous article, and come to mention it, we’ll need CoreLocation; so, add that too.
- Once that’s done add #import <QuartzCore/QuartzCore.h> to the top of the LIOfferViewController.m file. Now, add the following code to instantiate the image view and add it to our view:
UIImageView * imageView = [[UIImageView alloc] initWithFrame:CGRectMake(10, 120, 300, 300)]; [imageView.layer setBorderColor:[[UIColor whiteColor] CGColor]]; [imageView.layer setBorderWidth:2.f]; imageView.contentMode = UIViewContentModeScaleToFill; [self.view addSubview:imageView]; self.offerImageView = imageView;
Setting up our root view controller
Let’s jump to LIViewController now and start looking for beacons. We’ll start by telling LIViewController that LIOfferViewController exists and also that the view controller should act as a location manager delegate. Consider the following steps:
- Open LIViewController.h and add an import to the top of the file:
#import <CoreLocation/CoreLocation.h> #import "LIOfferViewController.h"
- Now, add the CLLocationManagerDelegate protocol to the declaration:
@interface LIViewController : UIViewController<CLLocationManagerDelegate>
- LIViewController also needs three things to manage its roll:
- A reference to the current offer on display so that we know to show only one offer at a time
- An instance of CLLocationManager for monitoring beacons
- A list of offers seen so that we only show each offer once
- Let’s add these three things to the interface in the CLViewController.m file (as they’re private instances). Change the LIViewController interface to look like this:
@interface LIViewController () @property (nonatomic, strong) CLLocationManager * locationManager; @property (nonatomic, strong) NSMutableDictionary * offersSeen; @property (nonatomic, strong) LIOfferViewController * currentOffer; @end
Configuring our location manager
Our location manager needs to be configured when the root view controller is first created, and also when the app becomes active. It makes sense therefore that we put this logic into a method. Our reset beacon method needs to do the following things:
- Clear down our list of offers seen
- Request permission to the user’s location
- Create a region and set our LIViewController instance as the delegate
- Create a beacon region and tell CLLocationManager to start ranging beacons
Let’s add the code to do this now:
-(void)resetBeacons { // Initialize the location manager. self.locationManager = [[CLLocationManager alloc] init]; self.locationManager.delegate = self; // Request permission. [self.locationManager requestAlwaysAuthorization]; // Clear the offers seen. self.offersSeen = [[NSMutableDictionary alloc] initWithCapacity:3]; // Create a region. NSUUID * regionId = [[NSUUID alloc] initWithUUIDString: @"8F0C1DDC-11E5-4A07-8910-425941B072F9"]; CLBeaconRegion * beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:regionId identifier:@"Mateys"]; // Start monitoring and ranging beacons. [self.locationManager stopRangingBeaconsInRegion:beaconRegion]; [self.locationManager startMonitoringForRegion:beaconRegion]; [self.locationManager startRangingBeaconsInRegion:beaconRegion]; }
Now, add the two calls to the reset beacon to ensure that the location manager is reset when the app is first started and then every time the app becomes active.
Let’s add this code now by changing the viewDidLoad method and adding the applicationDidBecomeActive method:
-(void)viewDidLoad { [super viewDidLoad]; [self resetBeacons]; } - (void)applicationDidBecomeActive:(UIApplication *)application { [self resetBeacons]; }
Wiring up CLLocationManagerDelegate
Now, we need to wire up the delegate methods of the CLLocationManagerDelegate protocol so that CLViewController can show the offer view when the beacons come into proximity.
The first thing we need to do is to set the background color of the view to show whether or not our app has been authorized to use the device location. If the authorization has not yet been determined, we’ll use orange. If the app has been authorized, we’ll use green. Finally, if the app has been denied, we’ll use red.
We’ll be using the locationManager:didChangeAuthorizationStatus delegate method to do this.
Let’s add the code now:
-(void)locationManager:(CLLocationManager *)manager
didChangeAuthorizationStatus:(CLAuthorizationStatus) status { switch (status) { case kCLAuthorizationStatusNotDetermined: { // Set a lovely orange background [self.view setBackgroundColor:[UIColor
colorWithRed:255.f/255.f green:147.f/255.f
blue:61.f/255.f alpha:1.f]]; break; } case kCLAuthorizationStatusAuthorized: { // Set a lovely green background. [self.view setBackgroundColor:[UIColor
colorWithRed:99.f/255.f green:185.f/255.f
blue:89.f/255.f alpha:1.f]]; break; } default: { // Set a dark red background. [self.view setBackgroundColor:[UIColor
colorWithRed:188.f/255.f green:88.f/255.f
blue:88.f/255.f alpha:1.f]]; break; } } }
The next thing we need to do is to save the battery life by stopping and starting the ranging of beacons when we’re within the region (except for when the app first starts). We do this by calling the startRangingBeaconsInRegion method with the locationManager:didEnterRegion delegate method and calling the stopRangingBeaconsInRegion method within the locationManager:didExitRegion delegate method.
Add the following code to do what we’ve just described:
-(void)locationManager:(CLLocationManager *)manager
didEnterRegion:(CLRegion *)region { [self.locationManager startRangingBeaconsInRegion:(CLBeaconRegion*)region]; } -(void)locationManager:(CLLocationManager *)manager
didExitRegion:(CLRegion *)region { [self.locationManager stopRangingBeaconsInRegion:(CLBeaconRegion*)region]; }
Showing the advert
To actually show the advert, we need to capture when a beacon is ranged by adding the locationManager:didRangeBeacons:inRegion delegate method to LIViewController. This method will be called every time the distance changes from an already discovered beacon in our region or when a new beacon is found for the region.
The implementation is quite long so I’m going to explain each part of the method as we write it.
Start by creating the method implementation as follows:
-(void)locationManager:(CLLocationManager *)manager
didRangeBeacons:(NSArray *)beacons inRegion: (CLBeaconRegion
*)region { }
We only want to show an offer associated with the beacon if we’ve not seen it before and there isn’t a current offer being shown. We do this by checking the currentOffer property. If this property isn’t nil, it means an offer is already being displayed and so, we need to return from the method.
The locationManager:didRangeBeacons:inRegion method gets called by the location manager and gets passed to the region instance and an array of beacons that are currently in range. We only want to see each advert once in a session and so need to loop through each of the beacons to determine if we’ve seen it before.
Let’s add a for loop to iterate through the beacons and in the beacon looping do an initial check to see if there’s an offer already showing:
for (CLBeacon * beacon in beacons) { if (self.currentOffer) return; >}
Our offersSeen property is NSMutableDictionary containing all the beacons (and subsequently offers) that we’ve already seen. The key consists of the major and minor values of the beacon in the format {major|minor}.
Let’s create a string using the major and minor values and check whether this string exists in our offersSeen property by adding the following code to the loop:
NSString * majorMinorValue = [NSString stringWithFormat: @"%@|%@", beacon.major, beacon.minor]; if ([self.offersSeen objectForKey:majorMinorValue]) continue;
If offersSeen contains the key, then we continue looping.
If the offer hasn’t been seen, then we need to add it to the offers that are seen, before presenting the offer.
Let’s start by adding the key to our offers that are seen in the dictionary and then preparing an instance of LIOfferViewController:
[self.offersSeen setObject:[NSNumber numberWithBool:YES] forKey:majorMinorValue]; LIOfferViewController * offerVc = [[LIOfferViewController alloc] init]; offerVc.modalPresentationStyle = UIModalPresentationFullScreen;
Now, we’re going prepare some variables to configure the offer view controller. Food offers show with a blue background while clothing offers show with a red background.
We use the major value of the beacon to determine the color and then find out the image and label based on the minor value:
UIColor * backgroundColor; NSString * labelValue; UIImage * productImage; // Major value 1 is food, 2 is clothing. if ([beacon.major intValue] == 1) { // Blue signifies food. backgroundColor = [UIColor colorWithRed:89.f/255.f green:159.f/255.f blue:208.f/255.f alpha:1.f]; if ([beacon.minor intValue] == 1) { labelValue = @"30% off sushi at the Japanese Kitchen."; productImage = [UIImage imageNamed:@"sushi.jpg"]; } else { labelValue = @"Buy one get one free at Tucci's Pizza."; productImage = [UIImage imageNamed:@"pizza.jpg"]; } } else { // Red signifies clothing. backgroundColor = [UIColor colorWithRed:188.f/255.f green:88.f/255.f blue:88.f/255.f alpha:1.f]; labelValue = @"50% off all ladies clothing."; productImage = [UIImage imageNamed:@"ladiesclothing.jpg"]; }
Finally, we need to set these values on the view controller and present it modally. We also need to set our currentOffer property to be the view controller so that we don’t show more than one color at the same time:
[offerVc.view setBackgroundColor:backgroundColor]; [offerVc.offerLabel setText:labelValue]; [offerVc.offerImageView setImage:productImage]; [self presentViewController:offerVc animated:YES completion:nil]; self.currentOffer = offerVc;
Dismissing the offer
Since LIOfferViewController is a modal view, we’re going to need a dismiss button; however, we also need some way of telling it to our root view controller (LIViewController). Consider the following steps:
- Add the following code to the LIViewController.h interface to declare a public method:
-(void)offerDismissed;
- Now, add the implementation to LIViewController.h. This method simply clears the currentOffer property as the actual dismiss is handled by the offer view controller:
-(void)offerDismissed { self.currentOffer = nil; }
- Now, let’s jump back to LIOfferViewController. Add the following code to the end of the viewDidLoad method of LIOfferViewController to create a dismiss button:
UIButton * dismissButton = [[UIButton alloc] initWithFrame:CGRectMake(60.f, 440.f, 200.f, 44.f)]; [self.view addSubview:dismissButton]; [dismissButton setTitle:@"Dismiss" forState:UIControlStateNormal]; [dismissButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; [dismissButton addTarget:self action:@selector(dismissTapped:) forControlEvents:UIControlEventTouchUpInside];
As you can see, the touch up event calls @selector(dismissTapped:), which doesn’t exist yet. We can get a handle of LIViewController through the app delegate (which is an instance of LIAppDelegate). In order to use this, we need to import it and LIViewController.
- Add the following imports to the top of LIOfferViewController.m:
#import "LIViewController.h" #import "LIAppDelegate.h"
- Finally, let’s complete the tutorial by adding the dismissTapped method:
-(void)dismissTapped:(UIButton*)sender { [self dismissViewControllerAnimated:YES completion:^{
LIAppDelegate * delegate =
(LIAppDelegate*)[UIApplication sharedApplication].delegate;
LIViewController * rootVc =
(LIViewController*)delegate. window.rootViewController; [rootVc offerDismissed]; }]; }
Now, let’s run our app. You should be presented with the location permission request as shown in the Requesting location permission figure, from the Understanding iBeacon permissions section. Tap on OK and then fire up the companion app. Play around with the Chapter 2 beacon configurations by turning them on and off. What you should see is something like the following figure:
Our app working with the companion OS X app
Remember that your app should only show one offer at a time and your beacon should only show each offer once per session.
Summary
Well done on completing your first real iBeacon powered app, which actually differentiates between beacons. In this article, we covered the real usage of UUID, major, and minor values. We also got introduced to the Core Location framework including the CLLocationManager class and its important delegate methods. We introduced the CLRegion class and discussed the permissions required when using CLLocationManager.
Resources for Article:
Further resources on this subject:
- Interacting with the User [Article]
- Physics with UIKit Dynamics [Article]
- BSD Socket Library [Article]