iOS iBeacon开发

应用场景

最近有个项目需求是,在展会的各个商家展位上配置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  //超过十米以外
    }
    

注:proximityUUIDmajorminor 这个三个属性组成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,NSLocationWhenInUseUsageDescriptionNSLocationAlwaysUsageDescription,请求地理位置权限。

开启Background Modes

image.png

具体代码

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) {
        
    }
    

你可能感兴趣的:(iOS iBeacon开发)