应用场景
最近有个项目需求是,在展会的各个商家展位上配置iBeacon设备,当用户(手机上安装公司APP)进入ibeacon覆盖的商家展位区域时,向后台上报该用户进出展位的时间,以便统计用户在各个展位的驻停时间,进而推算出用户在展会现场的大概动线及各个商家展位的人流量。
什么是iBeacon
- iBeacon是苹果公司开发的一种近场通讯协议,于2013年的WWDC开发者大会推出。当你的手持设备靠近一个Beacon基站时,设备就能够感应到Beacon信号,范围可以从几毫米到50米。简单的理解,iBeacon可以理解为一个发射信号的基站,当用户走进他的信号范围时,用户就可以接收到iBeacon设备的信号。
- iBeacon工作方式:iBeacon设备只使用低功耗蓝牙(BLE)的广告通信信道,以一定的时间间隔向外广播数据包,数据包中包含设备的唯一标志符。iOS设备可以通过特定的方式来Monitoring或Ranging来监测该iBeacon,从而实现自己的使用场景。
- iBeacon应用场景:比如笔者最近公司的项目需求是,在大型展会的各个商家展位上配置iBeacon设备,当用户(手机上安装公司APP)进入ibeacon覆盖的商家展位区域时,向后台上报该用户进出展位的时间,以便统计用户在各个展位的驻停时间,进而推算出用户在展会现场的大概动线及各个商家展位的人流量。
API介绍
虽然iBeacon的功能基于蓝牙功能的,但其却是一种定位技术,所以Apple把iBeacon功能集成到了CoreLocation框架里面了,所以要在使用类中导入CoreLocation。
1 首先介绍一下CLBeacon,iBeacon在CoreLocation框架中抽象为CLBeacon类,这个类有6个属性:
proximityUUID : 一级标识。每个公司、组织使用的 iBeacon 应该拥有同样的 proximityUUID
major :二级标识。比如在展会的应用场景中 展馆中的某个区拥有一样的major,来区分珠宝区,婚纱摄影区
minor : 三级标识。这个值就可以代表上面列子中具体的商家展位
accuracy : 与iBeacon设备的距离
rssi : 信号强度。强度值为负值,越接近0信号越强,等于0时无法获取到信号强度
-
proximity : 远近范围,一个枚举值
public enum CLProximity : Int { case unknown //未知,在IBeacon区域外 case immediate //在几厘米之内 case near //在几米之内 case far //超过十米以外 }
注:proximityUUID
、major
、minor
这个三个属性组成iBeacon
的唯一标识。
2 其次介绍一下CLBeaconRegion
,如果我们想扫描到周围的iBeacon设备,首先要知道周围ibeacon设备的proximityUUID
,major
,minor
,然后要实例化一个CLBeaconRegion
类来定义iBeacon的区域。这个类有三种实例化方法:
//仅使用proximityUUID来初始化区域,major,minor值将作为通配符。只要是区域内的iBeacon的proximityUUID与此proximityUUID相同,不管major, minor是什么值,都能被检测到。
public init(proximityUUID: UUID, identifier: String)
//使用proximityUUID和major来初始化区域,minor值将作为通配符。区域内的iBeacon的proximityUUID和major与此proximityUUID和major相同时,不论minor为何值,都能被检测到。
public init(proximityUUID: UUID, major: CLBeaconMajorValue, identifier: String)
//使用proximityUUID, major, minor来初始化,只能检测到区域内相同proximityUUID, major, minor的iBeacon设备。
public init(proximityUUID: UUID, major: CLBeaconMajorValue, minor: CLBeaconMinorValue, identifier: String)
// 周围有多个iBeacon设备的话就根据iBeacon设备的参数信息逐个CLBeaconRegion实例,这里只初始化一个来说明
let beaconRegion = CLBeaconRegion.init(proximityUUID: UUID.init(uuidString: "uuid")!, major: CLBeaconMajorValue.init(2), minor: CLBeaconMinorValue.init(7), identifier: "iden")
3 实例化完iBeacon设备区域之后,接下来就需要创建CLLocationManager
管理对象,实现其delegate代理,并实现CLLocationManagerDelegate
代理方法,来监控我们的iBeacon设备。CLLocationManager
有两种iBeacon的监控方式:
- Monitoring方式: 可以在前台、后台、进程杀掉、三种情况下监测到设备进入/离开iBeacon地理区域,触发对应的代理方法。但是触发的方法只能获取到你注册的beaconRegion的UUID,major,minor的信息(如果你beaconRegion初始化方法只有UUID信息,那么也只能取到UUID),具体的信号强度等信息是获取不到的,
self.locationManager.startMonitoring(for: beaconRegion)//开始监测 self.locationManager.stopMonitoring(for: beaconRegion)//停止监测 // 监测成功回调 func locationManager(_ manager: CLLocationManager, didStartMonitoringFor region: CLRegion) // 监测发生错误回调 func locationManager(_ manager: CLLocationManager, monitoringDidFailFor region: CLRegion?,withError error: Error) // 进入beaconRegion区域回调 func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) // 离开beaconRegion区域回调 func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) // 设备状态发生改变时回调,进入或者离开beaconRegion区域回调 func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion)
- Ranging方式: 可以用来检测某区域内的所有iBeacons,在前台调用,进程杀掉后不被调用
self.locationManager.startRangingBeacons(in: beaconRegion)//开始监测 self.locationManager.stopMonitoring(for: beaconRegion)//停止监测 //监测到该区域下的所有IBeacons 差不多1s刷新一次,这个方法会返回一个 CLBeacon 的数组,根据 CLBeacon 的 proximity 属性就可以判断设备和 beacon 之间的距离, 另外 CLBeacon 还有 accuracy 和 rssi 两个属性能提供更详细的距离数据 func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion) //监测失败回调 func locationManager(_ manager: CLLocationManager, rangingBeaconsDidFailFor region: CLBeaconRegion, withError error: Error)
iBeacon具体的开发
权限请求
在info.plist
中添加NSLocationAlwaysAndWhenInUseUsageDescription
,NSLocationWhenInUseUsageDescription
,NSLocationAlwaysUsageDescription
,请求地理位置权限。
开启Background Modes
具体代码
1 初始化CLLocationManager,注册iBeaconRegion区域
// 监听IBeacon的locationManager
var locationManager = {() -> CLLocationManager in
let manager = CLLocationManager.init()
return manager
}()
// 注册iBeaconRegion数组(可能用时监测很多个IBeacon区域)
var beaconRegionArr: [CLBeaconRegion] = []
func setupiBeacons() {
locationManager.delegate = self
let beaconRegion1 = CLBeaconRegion.init(proximityUUID: UUID.init(uuidString: uuidStr)!, major: CLBeaconMajorValue.init(1024), minor: CLBeaconMinorValue.init(1), identifier: "beacon1")
beaconRegion1.notifyOnEntry = true // 进入iBeaconRegion区域时,触发代理方法
beaconRegion1.notifyOnExit = true // 离开iBeaconRegion区域时,触发代理方法
beaconRegion1.notifyEntryStateOnDisplay = true
beaconRegionArr.append(beaconRegion1)
}
注意:locationManager的代理必须要设置为AppDelegate,因为苹果规定的这一机制,如果app处于关闭的状态,进入到了监控的区域,它只能在AppDelegate类中响应。如果我们在其他页面添加了监视区域,并将这些响应的代理方法都在写在了该页面,当程序处于关闭状态,只要你不打开App进入到这个页面,那么这些代理方法是不会被执行到的。(亲自踩过的坑)
2 开始监控IBeaconRegion区域
func startMonitorBeacons() {
//检测设备是否支持IBeacon监控功能
let availbleMonitor = CLLocationManager.isMonitoringAvailable(for: CLBeaconRegion.self)
//获取当前APP定位状态
let status = CLLocationManager.authorizationStatus()
if availbleMonitor {
if status == .authorizedAlways || status == .authorizedWhenInUse {
//开始监听所有的beaconRegion
for iBeaconRegion in beaconRegionArr {
//开始监测
self.locationManager.startMonitoring(for: iBeaconRegion)
self.locationManager.startRangingBeacons(in: iBeaconRegion)
}
}else if status == .notDetermined{
self.locationManager.requestAlwaysAuthorization()
}
}else{
//设备不支持
}
}
3 实现CLLocationManagerDelegate代理方法
- Monitoring监测代理方法
/// Monitoring监测代理方法 func locationManager(_ manager: CLLocationManager, didStartMonitoringFor region: CLRegion) { NSLog("---------Monitoring监控成功回调"); } func locationManager(_ manager: CLLocationManager, monitoringDidFailFor region: CLRegion?, withError error: Error) { NSLog("---------Monitoring监控失败回调"); } /// 进入iBeaconRegion区域触发该代理方法 func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) { if region.isKind(of: CLBeaconRegion.self) { let beaRegion: CLBeaconRegion = region as! CLBeaconRegion NSLog(" proximityUUID : %@",beaRegion.proximityUUID.uuidString); NSLog(" major :%ld",beaRegion.major!.intValue); NSLog(" minor :%ld",beaRegion.minor!.intValue); } } /// 离开iBeaconRegion区域触发该代理方法 func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) { if region.isKind(of: CLBeaconRegion.self) { let beaRegion: CLBeaconRegion = region as! CLBeaconRegion NSLog(" proximityUUID : %@",beaRegion.proximityUUID.uuidString); NSLog(" major :%ld",beaRegion.major!.intValue); NSLog(" minor :%ld",beaRegion.minor!.intValue); } }
- Ranging监测代理方法
/// Ranging成功对应回调函数 监测到iBeacon设备 func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion) { for beacon in beacons{ NSLog(" proximityUUID : %@",beacon.proximityUUID.uuidString); NSLog(" major :%ld",beacon.major.intValue); NSLog(" minor :%ld",beacon.minor.intValue); NSLog(" rssi :%ld",beacon.rssi); NSLog(" accuracy :%ld",beacon.accuracy); } } // Ranging有错误产生时的回调 func locationManager(_ manager: CLLocationManager, rangingBeaconsDidFailFor region: CLBeaconRegion, withError error: Error) { }