Takahiro Octopress Blog

-1から始める情弱プログラミング

噂のiOS7.1でiBeaconを試してみよう!!

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間の距離がどんどんローカルプッシュされてくることでしょう。
Centralアプリを起動していない状態でローカルプッシュが届く
その後、Peripheralアプリを停止してみましょう。しばらくするとCentralアプリにExit Regionというローカルプッシュが届くはずです。それを最後にローカルプッシュは来なくなります。
Beaconの検知終了

このように、確かにアプリをBackgroundで起動していなくてもBeaconを検知するようです。因みに、このときアプリがBackground起動しているのか確認したのですが、起動はしてないんですね〜。というかホームボタン2回タップでBackground起動しているアプリが見れるはずなのに、そこに出てないんですね〜。う〜ん。どういうことなんでしょうか??

まぁ、何はともあれ、iOS7.1からiBeaconの扱いが劇的に変わるってことは間違いないようです。

Comments