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) {
        
    }
    
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343