iBeacons和iOS:Rantmedia Hack Day

什么是iBeacon?

iBeacon是由Apple开发的协议,该协议利用低功耗蓝牙将信息广播到收听设备和应用程序。

iBeacon广播三个重要信息:其UUID,主要ID和次要ID。 这些用于识别单个信标,并允许应用程序在靠近信标的情况下做出适当响应。

例如,事件中心可以设置一些iBeacon并将它们配置为使用相同的UUID。 在这种情况下,UUID标识信标的所有者。 每个信标可​​以被赋予不同的主要或次要ID。 然后,应用程序可以侦听具有给定UUID的任何信标,并在进入信标覆盖的每个不同区域时执行操作。

对于我们的Hack Day,我们使用Android应用程序BeaconToy设置了多个具有不同值的信标。

iBeacon与iOS集成

苹果在其CoreLocation和CoreBluetooth框架中包括对iBeacons的支持。 如果要编写只需要侦听iBeacon的应用程序,则需要使用CoreLocation,而如果要从应用程序中作为iBeacon广播,则需要CoreBluetooth框架。 出于Hack Day的目的,我们正在编写一个仅监听和响应信标而不是广播的应用程序。

构建一个iBeacons应用

如果您想继续,可以找到我们在Github上构建的应用程序。

总览

该应用程序使用CoreLocation的CLLocationManager监视CLBeaconRegion。 输入信标区域时,它将开始对区域内的所有信标进行测距。 远程信标以其UUID,主要和次要ID以及相对接近程度显示在表格视图中。 当输入区域时,如果应用程序未激活,我们还使用后台信标监视来显示通知。

创建一个新项目,并使用UITableViewController设置基本UI。

设置CoreLocation

在开始监视之前,我们需要设置视图控制器以导入CoreLocation框架,并设置CLLocationManager并使表视图控制器符合CLLocationManagerDelegate协议:

  导入 UIKit 
导入 CoreLocation
   ViewControllerUITableViewControllerCLLocationManagerDelegate { 
   locationManager = CLLocationManager () 

覆盖 func viewDidLoad (){
超级 .viewDidLoad()
  locationManager.delegate = 自我 
  如果 CLLocationManager .authorizationStatus()!= .authorizedAlways { 
locationManager.requestAlwaysAuthorization()
} 其他 {
//已经授权
}
}

func locationManager (_管理器:CLLocationManager,didChangeAuthorization状态:CLAuthorizationStatus){
开关状态{
案例 .authorized总是:
//始终授予权限
案例 .authorizedWhenInUse:
//授予使用中权限
默认值
print(“未授予授权”) //正确处理
}
}
}

我们还需要为该应用的Info.plist中的位置权限提示提供值,以用于以下键:

  NSLocationAlwaysAndWhenInUseUsesDescription 
NSLocationWhenInUseUsesDescription
NSLocationUsageDescription
NSLocationAlwaysUsageDescription

监视信标区域

现在来看看有趣的东西。 一旦获得使用位置的许可,我们就可以开始监视信标区域。

我们需要设置一个CLBeaconRegion并告诉它要监视的UUID:

   uuid = UUID (uuidString:“ 0fdb9b40-cf82–4362-ba5d-246f094f5c2a”)! 
beaconRegion = CLBeaconRegion (proximityUUID:uuid,major:101,minor:2,identifier:uuid.uuidString)

我们将信标设置为使用以下值:

  UUID:0fdb9b40-cf82–4362-ba5d-246f094f5c2a 
专业编号:101
次要ID:2

您需要使用为信标设置的任何值。

接下来,我们告诉locationManager开始监视它:

  locationManager.startMonitoring( for :beaconRegion) 

我们需要知道何时进入和退出信标区域。 首次运行时,如果应用程序已经在信标范围之内,则不会发送进入事件(有关更多信息,请参见下面的“陷阱”部分)。 将以下委托回调实现添加到视图控制器:

  func locationManager (_管理器:CLLocationManager,didDetermineState状态:CLRegionState,区域:CLRegion){ 
打印(“确定区域\(区域)的状态”)

切换状态{
案例 .inside:
//在信标范围内
案例 .outside:
//超出信标范围
案例 .unknown:
//信标的范围未知
}
}
  func locationManager (_管理器:CLLocationManager,didEnterRegion区域:CLRegion){ 
打印(“ didEnter”)
}
  func locationManager (_管理器:CLLocationManager,didExitRegion区域:CLRegion){ 
打印(“ didExit”)
}

该应用程序现在将在进入或退出信标区域时接收事件。 当我们知道自己在该区域内时,可以使用它们来启动测距信标。

测距信标

布置信标是找出设备与信标之间相对距离的过程。 该框架通过对报告之间的接收信号强度指示器(RSSI以分贝或dBm为单位)进行平均来做到这一点。 越接近0 dbm,信号越好,这通常意味着信标越近。

在我们的应用程序中,一旦我们进入一个区域,就可以开始在其中定位信标:

  locationManager.startRangingBeacons( in :beaconRegion) 

这将触发locationManager开始为该区域内的所有信标生成测距报告。 我们需要实现另一个委托函数来接收那些报告:

  func locationManager (_管理器:CLLocationManager,didRangeBeacons信标:[CLBeacon], 区域:CLBeaconRegion){} 

此函数传入CLBeacon对象数组,其中包含有关已发现区域内任何信标的信息。 方便地,我们可以使用此数组来填充表格视图(您可以在Github上签出源代码以查看如何执行此操作)。

CLBeacon提供了一些属性来访问其信息,例如UUID,主要,次要等,但是最有趣的属性是它的接近性。 这是一个具有以下值的枚举:未知,远,近和立即。 如示例代码所示,我们可以使用这些值来构建测距仪。

当我们将设备从信标移开时,我们应该能够看到didRangeBeacons方法中报告的接近值的变化。

后台监控

因此,现在我们有了一个应用程序,该应用程序显示一个区域内的信标列表并显示其接近程度。 大! 我们该如何处理这些信息? 进入信标覆盖的区域时显示通知会很有用,例如进入商店并在我们的智能手机上收到最新交易?

幸运的是,CoreLocation为我们处理了信标区域的后台监控! 一旦调用startMonitoring( forCLBeaconRegion ),即使应用程序关闭,系统也将继续监视区域,而不仅仅是后台。 当发生事件(例如进入区域)时,iOS将唤醒或启动该应用程序,并给其几秒钟的时间来执行某些操作。 我们可以利用这段时间显示本地通知。 可以在Github上的示例应用程序中查看实现此目的的代码。

进行后台监视时需要牢记一些重要的注意事项。

  • 后台监视需要始终位置权限。 没有它,系统将不会在后台监视区域。
  • 您的应用程序在被激活后才有很短的时间来工作,因此不要做花费很长时间的事情,否则您的应用程序会因为不经常被唤醒而受到惩罚。

陷阱

iBeacons的工作方式以及它们如何与CoreLocation集成固有的一些吸引我们注意的地方。

位置权限

为了使iBeacons正常工作,该应用程序需要至少具有“在使用时”位置的权限。 对于后台区域监视,该应用将需要始终定位权限。 Apple建议您仅在后台监视为您的应用程序带来重大价值时才向应用程序请求“始终”权限。 像往常一样,由用户决定是否要授予该权限,因此您应该编写应用程序来处理这种情况,并提供一个UI来解释为什么需要该权限以及如果需要的话该行不通没有给。

进出事件

  func locationManager(_管理器:CLLocationManager,didEnterRegion区域:CLRegion) 
  func locationManager(_管理器:CLLocationManager,didExitRegion区域:CLRegion) 

每当进入或退出信标区域时(即设备进入/超出信标范围时),就会向位置管理器的代表发送这些事件。 刚开始时,即使我们处于信标范围内,我们也没有看到这些事件。

这是因为仅在状态之间发生转换时才发送事件。 例如,如果已经监视某个区域的设备在该区域之外,然后进入该区域,则将发送进入事件。 退出事件也是如此。 当设备开始监视时,该设备已在区域中时,该框架不会发送进入/退出事件。

您可以使用另一个函数来确定信标区域的状态:

  func locationManager(_管理器:CLLocationManager,didDetermineState状态:CLRegionState,区域:CLRegion) 

在框架开始监视后,一旦框架知道了信标的状态,就会将其发送给委托人。 状态具有三个值:内部,外部和未知。 我们使用此功能来了解应用程序启动时区域的初始状态,然后使用进入/退出事件来了解状态之间的转换何时发生。

内置定时

退出信标区域时,框架最多会在30秒内不发送退出事件。 这是为了防止在短期内暂时阻塞信标信号而导致错误退出事件的情况。 回想起来似乎很明显,但很有用。

该框架在优化蓝牙无线电的使用方面也起到了很大作用,以节省功耗并在iOS中成为良好的公民。 当信标不在活动范围内时,它们的事件将减慢并合并,尤其是在使用后台监视时。

最后的想法

在这次Hack Day中,我们了解了很多有关iBeacon的知识,从它们的本质,如何工作到在实际情况下如何发挥作用。 特别有趣的是,该技术在iOS生态系统中的集成程度以及实现起来的难易程度。

进一步阅读

这是来自我们的Rantmedia 2018年2月信标黑客日的一系列蓝牙信标的一部分-这里有2018年信标状态的概述,并且这里有一个Android实现的帖子。