iBeacon 是苹果公司在 iOS 7 中推出的一种近场定位技术,可以感知一个附近的 iBeacon 信标的存在。
当一个 iBeacon 兼容设备进入/退出一个 iBeacon 信标标识的区域时,iOS 和支持 iBeacon 的 app 就能得知这一信息,从而对用户发出相应的通知。
典型的应用场景例如博物馆实时推送附近展品的相关信息,商场内即时通知客户折扣信息等。苹果在 Apple Store 中也部署了 iBeacon 来推送优惠、活动信息。
![](http://www.cocoachina.com/cms/uploads/allimg/140530/8370_140530101454_1.png)
特点
iBeacon 基于低功耗蓝牙技术(Bluetooth Low Energy, BLE)这一开放标准,因此也继承了 BLE 的一些特点。
范围广
相比于 NFC 的数厘米的识别范围,iBeacon 的识别范围可以达到数十米,并且能够估计距离的远近。
兼容性
iBeacon 是基于 BLE 做的一个简单封装,因此大部分支持 BLE 的设备都可以兼容。
例如可以使用一个普通的蓝牙芯片作为信标,使用 Android 设备检测信标的存在。
低能耗
不少 beacon 实现宣称可以不依赖外部能源独立运行两年。
使用场景
我们以一个连锁商场的例子来讲解 iBeacon 的一个流程。在一个连锁商场中,店家需要在商场中的不同地方推送不同的优惠信息,比如服装和家居柜台推送的消息就很有可能不同。
当消费者走进某个商场时,会扫描到一个 beacon。这个 beacon 有三个标志符,proximityUUID 是一个整个公司(所有连锁商场)统一的值,可以用来标识这个公司,major 值用来标识特定的连锁商场,比如消费者正在走进的商场,minor 值标识了特定的一个位置的 beacon,例如定位到消费者正在门口。
这时商场的 app 会被系统唤醒,app 可以运行一个比较短的时间。在这段时间内,app 可以根据 beacon 的属性查询到用户的地理位置(通过查询服务器或者本地数据),例如在化妆品专柜,之后就可以通过一个 local notification 推送化妆品的促销信息。用户可以点击这次 local notification 来查看更详细的信息,这样一次促销行为就完成了。
API
我们来看下 iBeacon 具体怎么使用:
Beacon 的表示
iBeacon 本质上来说是一个位置(区域)信息,所以 Apple 把 iBeacon 功能集成在了 Core Location 里面。
iBeacon 信标在 Core Location 中表现为一个 CLBeacon,它圈定的范围则表现为 CLBeaconRegion,这是一个 CLRegion 的子类。
CLBeaconRegion 主要用三个属性来标识一个 iBeacon,proximityUUID、major 和 minor。
proximityUUID 是一个 NSUUID,用来标识公司,每个公司、组织使用的 iBeacon 应该拥有同样的 proximityUUID。
major 用来识别一组相关联的 beacon,例如在连锁超市的场景中,每个分店的 beacon 应该拥有同样的 major。
minor 则用来区分某个特定的 beacon。
这些属性如果不指定(即 nil),匹配的时候就会忽略这个属性。例如只指定 proximityUUID 的 CLBeaconRegion 可以匹配某公司的所有 beacons。
Monitoring
Apple 在 iOS 4 中增加了地理围栏 API,可以用来在设备进入/退出某个地理区域时获得通知,这些 API 包括 -startMonitoringForRegion:、-locationManager:didEnterRegion:、-locationManager:didExitRegion: 等。
CLBeaconRegion 作为 CLRegion 的子类也可以复用这些 API,这种检测 iBeacon 的方式叫做 monitoring。
使用这种方法可以在程序在后台运行时检测 iBeacon,但是只能同时检测 20 个 region,也不能推测设备与 beacon 的距离。
Ranging
除了使用地理围栏 API 的方式,Apple 还在 iOS 7 中新增加了 iBeacon 专用的检测方式,也就是 ranging。
通过 CLLocationManager 的 -startRangingBeaconsInRegion: 方法可以开始检测特定的 iBeacon。
当检测到 beacon 的时候,CLLocationManager 的 delegate 方法 -locationManager:didRangeBeacons:inRegion: 会被调用,通知调用者现在被检测到的 beacons。
这个方法会返回一个 CLBeacon 的数组,根据 CLBeacon 的 proximity 属性就可以判断设备和 beacon 之间的距离。
proximity 属性有四个可能的值,unknown、immediate、near 和 far。
另外 CLBeacon 还有 accuracy 和 rssi 两个属性能提供更详细的距离数据。
使用 iOS 设备作为 iBeacon
我们可以使用 Core Bluetooth 框架来广播特定的 payload 来让 iOS 设备成为一个 iBeacon。
这个 payload 可以由 CLBeaconRegion 的 -peripheralDataWithMeasuredPower: 方法来获取。
之后交给 CBPeripheralManager 广播出去就可以了。
需要注意的是,广播 iBeacon 信息的时候 app 必须在前台运行。
行为
iBeacon 的 API 并不十分复杂,但他的行为比较难弄清楚,特别是当应用运行在后台时,检测到 beacon 的时间延迟会让开发者难以推测。在做了一些实验和合理的推测后,我们得出了一些结论:
检测到 beacon 的时间跟设备进行蓝牙扫描的时间间隔有关,每当设备进行扫描的时候,就能发现 iBeacon region 的变化。
在 ranging 打开的情况下,设备会每秒钟做一次扫描,也就是说状态更新最多延迟一秒。
程序在后台运行,并且 monitoring 打开的时候,设备可能每隔数分钟做一次扫描。iOS 7 的响应速度较慢,iOS 7.1 有比较大的改善。
如果存在设置 notifyEntryStateOnDisplay=YES 的 beacon,iOS 会在屏幕点亮的时候(锁屏状态下按下 home 键,或者因为收到推送点亮等)进行一次扫描。
设备重启并不影响 iBeacon 后台检测的执行。
iOS 7 中,在多任务界面中杀掉程序会终止 iBeacon 检测的执行,iOS 7.1 上改变了这一行为,被杀掉的 app 还可以继续进行 iBeacon 检测。
原文链接 http://www.cocoachina.com/ios/20140530/8614.html
================
1. iBeacon的使用
1、iBeacon的使用是基于蓝牙和定位的,所以我们需要先到入两个库:
#import <CoreLocation/CoreLocation.h>
#import <CoreBluetooth/CoreBluetooth.h>
在iOS8之后,苹果改变了定位的开启方式,需要在plist文件中加入字段NSLocationAlwaysUsageDescription(允许一直开启定位)
![](http://upload-images.jianshu.io/upload_images/1863665-e0183a69ab206a86.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/700)
129603-ab293305d84223f4.png.jpg
2.直接上代码,以下代码包含广播自身与搜索附近广播:
#define UUID [[NSUUID alloc]initWithUUIDString:@"E2C56DB5-DFFB-48D2-B060-D0F5A71096E0"] // uuid可替换为自己需要的
@interface ViewController ()<CBPeripheralManagerDelegate,CLLocationManagerDelegate>
@property (strong, nonatomic) CLLocationManager *locationManager;
@property (strong, nonatomic) CLBeaconRegion *beaconRegion;
@property (strong, nonatomic) CBPeripheralManager *peripheraManager;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self beaconBroadCasting]; // 广播自身
[self beaconMonitoring]; // 扫描附近广播
}
#pragma mark - BroadCast
- (void)beaconBroadCasting{
// 创建并广播信号
if (!_peripheraManager)
{
_peripheraManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];
}
else
{
_peripheraManager.delegate = self;
}
}
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral{
if (peripheral.state == CBPeripheralManagerStatePoweredOn) {
NSDictionary *peripheralData = nil;
CLBeaconRegion * region = [[CLBeaconRegion alloc] initWithProximityUUID:UUID major:0 minor:1 identifier:[UUID UUIDString]];// UUID、major、minor将会在后面解释
peripheralData = [region peripheralDataWithMeasuredPower:nil];
if(peripheralData)
{
[self.peripheraManager startAdvertising:peripheralData];
}
}
}
#pragma mark - Monitor
- (void)beaconMonitoring{
self.beaconRegion = [[CLBeaconRegion alloc]initWithProximityUUID:UUID identifier:[UUID UUIDString]];
self.locationManager = [[CLLocationManager alloc]init];
self.locationManager.delegate = self;
[self.locationManager startRangingBeaconsInRegion:self.beaconRegion];
}
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region{
// 发现有iBeacon设备进入扫描范围回调
}
- (void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray<CLBeacon *> *)beacons inRegion:(CLBeaconRegion *)region{
// 扫描到iBeacon设备回调,扫描到的beacon存在数组beacons中
// 打印iBeacon信息
for (CLBeacon *beacon in beacons) {
// iBeacon参数将在后面介绍
}
}
2.iBeacon的参数
uuid 唯一标识,唯一标识此类iBeacon
major 主要值
minor 次要值
主要值与次要值能够使你区分使用相同UUID的不同iBeacon设备。(在将手机模拟为iBeacon广播时,可将一些信息作为major或者minor广播)注意major与minor为16 位无符号整数。
proximity 远近范围,包含三种情况:
CLProximityFar 10米以外
CLProximityImmediate 几米范围之内
CLProximityNear 几厘米范围内
rssi 信号强度,为负值,越接近0表示信号强度越大,距离越近