blob: 9b7c08e6d6851f97d9a5e415a50dcdd895f8f7e1 [file] [log] [blame]
Nick Toumpelis02e5ae62013-10-06 16:36:34 +02001//
2// NTViewController.m
3// HiBeacons
4//
5// Created by Nick Toumpelis on 2013-10-06.
6// Copyright (c) 2013 Nick Toumpelis.
7//
8// Permission is hereby granted, free of charge, to any person obtaining a copy
9// of this software and associated documentation files (the "Software"), to deal
10// in the Software without restriction, including without limitation the rights
11// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12// copies of the Software, and to permit persons to whom the Software is
13// furnished to do so, subject to the following conditions:
14//
15// The above copyright notice and this permission notice shall be included in
16// all copies or substantial portions of the Software.
17//
18// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24// THE SOFTWARE.
25//
26
27#import "NTViewController.h"
28
29static NSString * const kUUID = @"00000000-0000-0000-0000-000000000000";
30static NSString * const kIdentifier = @"SomeIdentifier";
Nick Toumpelis6d4ea722013-10-06 21:45:25 +020031static NSString * const kCellIdentifier = @"BeaconCell";
Nick Toumpelis02e5ae62013-10-06 16:36:34 +020032
33@interface NTViewController ()
34
35@property (nonatomic, strong) CLLocationManager *locationManager;
36@property (nonatomic, strong) CLBeaconRegion *beaconRegion;
37@property (nonatomic, strong) CBPeripheralManager *peripheralManager;
Nick Toumpelis6d4ea722013-10-06 21:45:25 +020038@property (nonatomic, strong) NSArray *detectedBeacons;
Nick Toumpelis02e5ae62013-10-06 16:36:34 +020039
40@end
41
42@implementation NTViewController
43
44- (void)viewDidLoad
45{
46 [super viewDidLoad];
47
48 [self.advertisingSwitch addTarget:self
49 action:@selector(changeAdvertisingState:)
50 forControlEvents:UIControlEventValueChanged];
51 [self.rangingSwitch addTarget:self
52 action:@selector(changeRangingState:)
53 forControlEvents:UIControlEventValueChanged];
54}
55
56#pragma mark - Beacon ranging
57- (void)createBeaconRegion
58{
59 if (self.beaconRegion)
60 return;
61
62 NSUUID *proximityUUID = [[NSUUID alloc] initWithUUIDString:kUUID];
63 self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:proximityUUID identifier:kIdentifier];
64}
65
66- (void)turnOnRanging
67{
68 NSLog(@"Turning on ranging...");
69
70 if (![CLLocationManager isRangingAvailable]) {
71 NSLog(@"Couldn't turn on ranging: Ranging is not available.");
72 self.rangingSwitch.on = NO;
73 return;
74 }
75
76 if (self.locationManager.rangedRegions.count > 0) {
77 NSLog(@"Didn't turn on ranging: Ranging already on.");
78 return;
79 }
80
81 [self createBeaconRegion];
82 [self.locationManager startRangingBeaconsInRegion:self.beaconRegion];
83
84 NSLog(@"Ranging turned on for region: %@.", self.beaconRegion);
85}
86
87- (void)changeRangingState:sender
88{
89 UISwitch *theSwitch = (UISwitch *)sender;
90 if (theSwitch.on) {
91 [self startRangingForBeacons];
92 } else {
93 [self stopRangingForBeacons];
94 }
95}
96
97- (void)startRangingForBeacons
98{
99 self.locationManager = [[CLLocationManager alloc] init];
100 self.locationManager.delegate = self;
Nick Toumpelis02e5ae62013-10-06 16:36:34 +0200101
102 [self turnOnRanging];
103}
104
105- (void)stopRangingForBeacons
106{
107 if (self.locationManager.rangedRegions.count == 0) {
108 NSLog(@"Didn't turn off ranging: Ranging already off.");
109 return;
110 }
111
112 [self.locationManager stopRangingBeaconsInRegion:self.beaconRegion];
113
Nick Toumpelis6d4ea722013-10-06 21:45:25 +0200114 self.detectedBeacons = nil;
115 [self.beaconTableView reloadData];
116
Nick Toumpelis02e5ae62013-10-06 16:36:34 +0200117 NSLog(@"Turned off ranging.");
118}
119
120#pragma mark - Beacon ranging delegate methods
121- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
122{
123 if (![CLLocationManager locationServicesEnabled]) {
124 NSLog(@"Couldn't turn on ranging: Location services are not enabled.");
125 self.rangingSwitch.on = NO;
126 return;
127 }
128
129 if ([CLLocationManager authorizationStatus] != kCLAuthorizationStatusAuthorized) {
130 NSLog(@"Couldn't turn on ranging: Location services not authorised.");
131 self.rangingSwitch.on = NO;
132 return;
133 }
134
135 self.rangingSwitch.on = YES;
136}
137
138- (void)locationManager:(CLLocationManager *)manager
139 didRangeBeacons:(NSArray *)beacons
140 inRegion:(CLBeaconRegion *)region {
141 if ([beacons count] == 0) {
142 NSLog(@"No beacons found nearby.");
Nick Toumpelis6d4ea722013-10-06 21:45:25 +0200143 } else {
Nick Toumpelisb2b3f202013-10-20 13:31:00 +0200144 NSLog(@"Found %lu %@.", (unsigned long)[beacons count], [beacons count] > 1 ? @"beacons" : @"beacon");
Nick Toumpelis02e5ae62013-10-06 16:36:34 +0200145 }
146
Nick Toumpelis6d4ea722013-10-06 21:45:25 +0200147 self.detectedBeacons = beacons;
148 [self.beaconTableView reloadData];
Nick Toumpelis02e5ae62013-10-06 16:36:34 +0200149}
150
151#pragma mark - Beacon advertising
152- (void)turnOnAdvertising
153{
Nick Toumpelisb2b3f202013-10-20 13:31:00 +0200154 if (self.peripheralManager.state != CBPeripheralManagerStatePoweredOn) {
Nick Toumpelis02e5ae62013-10-06 16:36:34 +0200155 NSLog(@"Peripheral manager is off.");
156 self.advertisingSwitch.on = NO;
157 return;
158 }
159
Nick Toumpelis6d4ea722013-10-06 21:45:25 +0200160 time_t t;
161 srand((unsigned) time(&t));
Nick Toumpelis02e5ae62013-10-06 16:36:34 +0200162 CLBeaconRegion *region = [[CLBeaconRegion alloc] initWithProximityUUID:self.beaconRegion.proximityUUID
Nick Toumpelis6d4ea722013-10-06 21:45:25 +0200163 major:rand()
164 minor:rand()
Nick Toumpelis02e5ae62013-10-06 16:36:34 +0200165 identifier:self.beaconRegion.identifier];
166 NSDictionary *beaconPeripheralData = [region peripheralDataWithMeasuredPower:nil];
167 [self.peripheralManager startAdvertising:beaconPeripheralData];
Nick Toumpelisb2b3f202013-10-20 13:31:00 +0200168
169 NSLog(@"Turning on advertising for region: %@.", region);
Nick Toumpelis02e5ae62013-10-06 16:36:34 +0200170}
171
172- (void)changeAdvertisingState:sender
173{
174 UISwitch *theSwitch = (UISwitch *)sender;
175 if (theSwitch.on) {
176 [self startAdvertisingBeacon];
177 } else {
178 [self stopAdvertisingBeacon];
179 }
180}
181
182- (void)startAdvertisingBeacon
183{
184 NSLog(@"Turning on advertising...");
185
186 [self createBeaconRegion];
187
188 if (!self.peripheralManager)
189 self.peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil options:nil];
190
191 [self turnOnAdvertising];
192}
193
194- (void)stopAdvertisingBeacon
195{
196 [self.peripheralManager stopAdvertising];
197
198 NSLog(@"Turned off advertising.");
199}
200
201#pragma mark - Beacon advertising delegate methods
202- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheralManager error:(NSError *)error
203{
204 if (error) {
205 NSLog(@"Couldn't turn on advertising: %@", error);
206 self.advertisingSwitch.on = NO;
207 return;
208 }
209
210 if (peripheralManager.isAdvertising) {
211 NSLog(@"Turned on advertising.");
212 self.advertisingSwitch.on = YES;
213 }
214}
215
216- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheralManager
217{
Nick Toumpelisb2b3f202013-10-20 13:31:00 +0200218 if (peripheralManager.state != CBPeripheralManagerStatePoweredOn) {
Nick Toumpelis02e5ae62013-10-06 16:36:34 +0200219 NSLog(@"Peripheral manager is off.");
220 self.advertisingSwitch.on = NO;
221 return;
222 }
223
224 NSLog(@"Peripheral manager is on.");
225 [self turnOnAdvertising];
226}
227
Nick Toumpelis6d4ea722013-10-06 21:45:25 +0200228#pragma mark - Table view functionality
229- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
230{
231 CLBeacon *beacon = self.detectedBeacons[indexPath.row];
Nick Toumpelis142771e2013-10-06 19:09:24 +0200232
Nick Toumpelis6d4ea722013-10-06 21:45:25 +0200233 UITableViewCell *defaultCell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
234 reuseIdentifier:kCellIdentifier];
235
236 defaultCell.textLabel.text = beacon.proximityUUID.UUIDString;
237
238 NSString *proximityString;
239 switch (beacon.proximity) {
240 case CLProximityNear:
241 proximityString = @"Near";
242 break;
243 case CLProximityImmediate:
244 proximityString = @"Immediate";
245 break;
246 case CLProximityFar:
247 proximityString = @"Far";
248 break;
249 case CLProximityUnknown:
250 default:
251 proximityString = @"Unknown";
252 break;
Nick Toumpelis142771e2013-10-06 19:09:24 +0200253 }
Nick Toumpelis6d4ea722013-10-06 21:45:25 +0200254 defaultCell.detailTextLabel.text = [NSString stringWithFormat:@"%@, %@ • %@ • %f • %li",
255 beacon.major.stringValue, beacon.minor.stringValue, proximityString, beacon.accuracy, (long)beacon.rssi];
256 defaultCell.detailTextLabel.textColor = [UIColor grayColor];
257
258 return defaultCell;
Nick Toumpelis142771e2013-10-06 19:09:24 +0200259}
260
Nick Toumpelis6d4ea722013-10-06 21:45:25 +0200261- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
262{
263 return 1;
264}
265
266- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
267{
268 return self.detectedBeacons.count;
269}
270
271- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
272{
273 return @"Detected beacons";
Nick Toumpelis142771e2013-10-06 19:09:24 +0200274}
275
Nick Toumpelis02e5ae62013-10-06 16:36:34 +0200276@end