iOS7.1ならアプリのBackground起動も必要なし!?
さて、本日は最近いろいろなところで取り上げられているiOS7.1でのiBeaconについて実際に試してみました。
iOS7から導入されたiBeaconですが、これまではアプリをBackgroundで起動していなければBeaconを検知することができませんでした。しかし、iOS7.1からアプリをBackgroundで起動していなくてもBeaconを検知できるようになったとビッグニュースになりました。でも、本当にそうなんでしょうか?と疑問に思った筆者はSampleアプリで試してみました。
下記がソースです。
まずはBeacon機器としてiPadを用いるためにPeripheralソースから書きます。
とりあえずProject作成の手順は
1: BeaconPeripheralという名称で新規Project作成
2: UIViewControllerを追加(ViewControllerという名称で作成しました)
3: CoreBluetoothとCoreLocationライブラリを追加
として下さい。
ViewController.hのソース
1
2
3
4
5
6
7
| #import <UIKit/UIKit.h>
#import <CoreBluetooth/CoreBluetooth.h>
#import <CoreLocation/CoreLocation.h>
@interface ViewController : UIViewController<CBPeripheralManagerDelegate>
@end
|
ViewController.mのソース
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
| #import "ViewController.h"
@interface ViewController ()
@property (strong, nonatomic) IBOutlet CBPeripheralManager *peripheralManager;
@property (strong, nonatomic) IBOutlet NSUUID *proximityUUID;
@end
// 省略
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = @"Peipheral";
self.proximityUUID = [[NSUUID alloc] initWithUUIDString:@"8D4DB809-032F-4771-96F3-99BD5C25F924"];
self.peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil options:nil];
if (self.peripheralManager.state == CBPeripheralManagerStatePoweredOn) {
[self startAdvertising];
}
}
- (void)startAdvertising
{
CLBeaconRegion *beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID: self.proximityUUID
major:1
minor:2
identifier:@"com.test.ibeaconSample"];
NSDictionary *beaconPeripheralData = [beaconRegion peripheralDataWithMeasuredPower:nil];
[self.peripheralManager startAdvertising:beaconPeripheralData];
}
|
次に、Beaconを検知するCentralのソースを書きます。
Projectの作成手順はPeripheralとほぼ同じで、
1: BeaconCentralという名称で新規Project作成
2: UIViewControllerを追加(ViewControllerという名称で作成しました)
3: CoreLocationライブラリを追加
として下さい。
ViewController.hのソース
1
2
3
4
5
6
| #import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
@interface ViewController : UIViewController<CLLocationManagerDelegate>
@end
|
ViewController.mのソース
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
| #import "ViewController.h"
@interface ViewController ()
@property (strong, nonatomic) IBOutlet CLLocationManager *locationManager;
@property (strong, nonatomic) IBOutlet NSUUID *proximityUUID;
@property (strong, nonatomic) IBOutlet CLBeaconRegion *beaconRegion;
@property (strong, nonatomic) IBOutlet CLBeacon *nearestBeacon;
@end
// initWithNibNameは省略
- (void)viewDidLoad
{
[super viewDidLoad];
if ([CLLocationManager isMonitoringAvailableForClass:[CLCircularRegion class]]) {
self.locationManager = [CLLocationManager alloc] init];]
self.locationManager.delegate = self;
self.proximityUUID = [[NSUUID alloc] initWithUUIDString:@"8D4DB809-032F-4771-96F3-99BD5C25F924"];
self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID: self.proximityUUID identifier:@"com.test.ibeaconSample"];
[self.locationManager startMonitoringForRegion: self.beaconRegion];
} else {
NSLog(@"お使いの端末ではiBeaconを利用できません。");
}
}
// 領域計測が開始した場合
- (void)locationManager:(CLLocationManager *)manager didStartMonitoringForRegion:(CLRegion *)region
{
[self sendLocalNotificationForMessage:@"Start Monitoring Region"];
}
// 指定した領域に入った場合
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
[self sendLocalNotificationForMessage:@"Enter Region"];
if ([region isMemberOfClass:[CLBeaconRegion class]] && [CLLocationManager isRangingAvailable]) {
[self.locationManager startRangingBeaconsInRegion:(CLBeaconRegion *)region];
}
}
// 指定した領域から出た場合
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region
{
[self sendLocalNotificationForMessage:@"Exit Region"];
if ([region isMemberOfClass:[CLBeaconRegion class]] && [CLLocationManager isRangingAvailable]) {
[self.locationManager stopRangingBeaconsInRegion:(CLBeaconRegion *)region];
}
}
// Beacon信号を検出した場合
- (void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region
{
if (beacons.count > 0) {
self.nearestBeacon = beacons.firstObject;
NSString *rangeMessage;
switch (self.nearestBeacon.proximity) {
case CLProximityImmediate:
rangeMessage = @"Range Immediate";
break;
case CLProximityNear:
rangeMessage = @"Range Near";
break;
case CLProximityFar:
rangeMessage = @"Range Far";
break;
default:
rangeMessage = @"Range Unknown";
break;
}
NSString *msg = [[NSString alloc] initWithFormat:@"%f [m]", self.nearestBeacon.accuracy];
[self sendLocalNotificationForMessage:msg];
}
}
// 領域観測に失敗した場合
- (void)locationManager:(CLLocationManager *)manager monitoringDidFailForRegion:(CLRegion *)region withError:(NSError *)error
{
[self sendLocalNotificationForMessage:@"Exit Region"];
}
// ローカルプッシュの処理
- (void)sendLocalNotificationForMessage:(NSString *)message
{
UILocalNotification *localNotification = [UILocalNotification new];
localNotification.alertBody = message;
localNotification.fireDate = [NSDate date];
localNotification.soundName = UILocalNotificationDefaultSoundName;
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
}
|
これでソースは完了です。
ソースを下記にアップしましたのでダウンロードしたい方はぜひ…。
Centralソース
Peripheralソース
まずはアプリをBackgroundで起動させているときの動作を見てみてください。
動作確認時には必ずCentralを先に起動する必要があります。約5秒後にPeripheralを起動して下さい。Centralの方にローカルプッシュが届くはずです。
続いて、アプリがBackground起動していない場合を見てみましょう。PeripheralアプリとCentralのアプリの両方を停止状態にします。(先ほどの状態から続けて実験する場合は、必ずCentralアプリ側にExit Regionというメッセージでローカルプッシュが届いてからにしてください。)
さて、Peripheralアプリを起動しましょう。しばらくするとCentralアプリを起動していないにも関わらず、ローカルプッシュが届くはずです。そして、Beacon間の距離がどんどんローカルプッシュされてくることでしょう。
その後、Peripheralアプリを停止してみましょう。しばらくするとCentralアプリにExit Regionというローカルプッシュが届くはずです。それを最後にローカルプッシュは来なくなります。
このように、確かにアプリをBackgroundで起動していなくてもBeaconを検知するようです。因みに、このときアプリがBackground起動しているのか確認したのですが、起動はしてないんですね〜。というかホームボタン2回タップでBackground起動しているアプリが見れるはずなのに、そこに出てないんですね〜。う〜ん。どういうことなんでしょうか??
まぁ、何はともあれ、iOS7.1からiBeaconの扱いが劇的に変わるってことは間違いないようです。