iBeacon开发之IoT&Swift&OC&iOS

前言

蓝牙设备想必大家都很熟悉了,相关的开发资料也是很多了,这里就不必多说了
iOS蓝牙的开发专题
iOS蓝牙开发(一)蓝牙相关基础知识
iOS蓝牙开发(二)ios连接外设的代码实现
iOS蓝牙开发(三)app作为外设被连接的实现
iOS蓝牙开发(四)BabyBluetooth蓝牙库介绍

iBeacon 是苹果公司2013年9月发布的移动设备用OS(iOS7)上配备的新功能。其工作方式是,配备有 低功耗蓝牙(BLE)通信功能的设备使用BLE技术向周围发送自己特有的ID,接收到该ID的应用软件会根据该ID采取一些行动。比如,在店铺里设置iBeacon通信模块的话,便可让iPhone和iPad上运行一资讯告知服务器,或者由服务器向顾客发送折扣券及进店积分。此外,还可以在家电发生故障或停止工作时使用iBeacon向应用软件发送资讯。
苹果 WWDC 14 之后,对 iBeacon 加大了技术支持和对其用于室内地图的应用有个更明确的规划。苹果公司公布了 iBeacon for Developers 和 Maps for Developers 等专题页面。

应用场景
  • 侦测iPhone进入或者离开某个iBeacon的范围触发事件
  • 侦测iPhone跟iBeacon的距离,依据不同的距离触发事件
    1.比如,当你在国家博物馆参观的时候,当你走进《四羊方尊》前,APP侦测到装在这个藏品展牌底座的iBeacon,于是APP就开始自动播放相关的介绍,透过AirPods传进你的耳朵,这样的讲解真的也算是一种创新!
    2.再比如,在地下车库按照三点确定一个稳定区域的设计,布置好iBeacon设备,也能设计出一套室内导航的方案!
如何获得

如何去获得这样的设备呢?其实你打开某宝某多某东,这类的产品数不胜数。
其实你只是在开发阶段,也可以自己去模拟一个iBeacon的设备。因为做iOS开发的我们都有一台iPhone&Mac,高版本的设备都有变身成iBeacon的蓝牙(bluetooth)能力。

将Mac或者iPhone变成iBeacon

-方法1: [Apple官方文档-将iPhone变成iBeacon(https://developer.apple.com/documentation/corelocation/turning_an_ios_device_into_an_ibeacon_device)

  • 方法2:可以下载一个APP将iPhone/iPad变成iBeacon设备下载APP

    IMG_9119.PNG

  • 我们可以下载别人已经开发好的应用,安装到我们的Mac上:MactsAsBeacon
    然后再去设定它的UUID、major、minor,然后再点击Start按钮,此时你的Mac就配置了一个iBeacon设备,开始发出讯号。

image.png

如果你想修改UUID,可以使用下列方法:
1.打开终端(terminal)
2.输入

uuidgen

3.回车按键


image.png

ps: 该项目比较老旧,运行会报错,
解决方案:
在Info.plist文件中,右键单击空白区域,选择 "Add Row" 添加一行。
在新添加的行中,将键名设置为NSBluetoothAlwaysUsageDescription。
在该键的值字段中,提供一条简明扼要的描述,说明您的应用程序为何需要蓝牙权限。例如,您可以写一句话,解释您的应用程序如何使用蓝牙,例如:"用于搜索和连接附近的蓝牙设备"。
保存Info.plist文件并重新构建您的应用程序。
这样做后,您的应用程序将在请求蓝牙权限时显示您提供的描述,以便用户了解为什么需要这些权限,并可以选择允许或拒绝访问。

请确保您的应用程序遵循隐私权规定,并提供清晰的权限描述,以确保用户的隐私权得到尊重。

iBeacon的参数相关

每个设备都有UUID(128bit),major(16bit),minor(16bit);因此我们可以透过这三个标识区分不同的iBeacon。

  • 让设备具有相同的UUID & major,利用minor区分
  • 让设备具有相同的UUID,利用major & minor区分
  • 利用UUID,major,minor来区分
    如果不是很复杂的需求,其实前两种区分即可,比如上述的博物馆游览;如果你有分店(UUID)、产品分类(major)和不同产品(minor),就可以用第三种方案。
开发 iBeacon 的 iOS App

这里有官方的相关参考说明
Determining the proximity to an iBeacon device
1. 加入获取位置的权限描述说明
依据Apple以下表格的说明,为了侦测iPhone进入某个iBeacon范围,量测iBeacon间的距离,我们需要在info.plist配置里加入以下两种权限的描述说明:
Apple官方说明:Choosing the Location Services Authorization to Request

image.png

Privacy — Location Always and When In Use Usage Description 
为了获取博物馆的展品iBeacon信号,请允许获取你的位置
Privacy — Location When In Use Usage Description
为了获取博物馆的展品iBeacon信号,请允许获取你的位置

2. 建立 CLLocationManager,将 controller 设置为 CLLocationManager 的 delegate 且 要求获取位置的权限

import CoreLocation

class ViewController: UIViewController {
   var locationManager: CLLocationManager!
   override func viewDidLoad() {
      super.viewDidLoad()
      locationManager = CLLocationManager()
      locationManager.delegate = self
      locationManager.requestAlwaysAuthorization()
}
extension ViewController: CLLocationManagerDelegate {
}

3. 开始侦测iBeacon

func monitorBeacons() {
   if CLLocationManager.isMonitoringAvailable(for: CLBeaconRegion.self) {
      let proximityUUID = UUID(uuidString: "B0702880-A295-A8AB-F734-031A98A512DE")
      let beaconId = "deeplove"
      let region = CLBeaconRegion(proximityUUID: proximityUUID!, identifier: beaconId)
      locationManager.startMonitoring(for: region)
   }
}
override func viewDidLoad() {
   super.viewDidLoad()
   locationManager = CLLocationManager()
   locationManager.delegate = self
   locationManager.requestAlwaysAuthorization()
   monitorBeacons()
}

说明
(1) 利用CLBeaconRegion指定你想要侦测的iBeacon
Apple官方文档:CLBeaconRegion

let region = CLBeaconRegion(proximityUUID: proximityUUID!, identifier: beaconId)

产生CLBeaconRegion的方法有三种:

public init(proximityUUID: UUID, identifier: String)
public init(proximityUUID: UUID, major: CLBeaconMajorValue, identifier: String)
public init(proximityUUID: UUID, major: CLBeaconMajorValue, minor: CLBeaconMinorValue, identifier: String)

在刚刚的例子里,我们假设目前只有一个iBeacon设备,因此利用UUID来区分设备足以,不需要用到major和minor(ps:请记得UUID要和刚刚在Mac iBeacon APP上设定的UUID一致,如此才能侦测到化身iBeacon的Mac)

let region = CLBeaconRegion(proximityUUID: proximityUUID!, identifier: beaconId)

(2) 开始侦测是否进入/离开iBeacon的范围

locationManager.startMonitoring(for: region)

4. 定义CLLocationManagerDelegate 的 function 侦测进入/离开iBeacon的范围

extension ViewController: CLLocationManagerDelegate {
   func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
      print("enter region")
   }
   func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
      print("exit region")
   }
}

5. 计算距离
有时候我们还想进一步知道iPhone和iBeacon之间的距离,Apple将距离粗略的分为四个等级,由近及远:Immediate 、Near、Far、Unknown

image.png

比方在看博物馆展览时,我们希望走到展品前时 App 才呈现相关的资讯,於是我们可从程式判断距离為 Immediate 时显示展品资讯,如此才不会距离画作还有一大段距离,连画都看不清楚时就奇怪地显示展品资讯。

extension ViewController: CLLocationManagerDelegate {
   func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
      print("enter region")
      if CLLocationManager.isRangingAvailable() {
         locationManager.startRangingBeacons(in: region as! CLBeaconRegion)
      }
   }
   func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
      print("exit region")
      locationManager.stopRangingBeacons(in: region as! CLBeaconRegion)
   }
}

说明:

利用 startRangingBeacons & stopRangingBeacons 开始 / 停止侦测 iPhone & beacon 的距离。我们在 locationManager(_:didEnterRegion:) 裡才开始侦测距离,因為侦测距离比较耗电,它将不断地量测更新距离。

不过有一点值得注意的,实测时发现 locationManager(:didEnterRegion:) 有可能没被触发,进而让程式不会呼叫 startRangingBeacons,因此永远不会侦测到 iPhone 已经在 beacon 旁边。比方若你原本就在 beacon 范围裡,locationManager(:didEnterRegion:) 将不会被触发。因此测试时最好一开始远离 beacon,在 beacon 范围之外,然后再慢慢地靠近 beacon,进入 beacon 的范围,这样较容易触发 locationManager(_:didEnterRegion:)。(ps: 若想确保我们一定能侦测到 iPhone 就在 beacon 旁边,另一种做法是一开始就同时呼叫 startMonitoring & startRangingBeacons)

let region = CLBeaconRegion(proximityUUID: proximityUUID!, identifier: beaconId)
locationManager.startMonitoring(for: region)
locationManager.startRangingBeacons(in: region)

6. 定义 CLLocationManagerDelegate 的 function 量测距离。
从 CLBeacon 物件的 proximity 判断距离。当我们在 beacon 附近时,此 function 将不断被呼叫,告诉我们最新的距离资讯。

func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion) {
   if beacons.count > 0 {
      let nearestBeacon = beacons[0]
      print(nearestBeacon.proximityUUID, nearestBeacon.major, nearestBeacon.minor)
      switch nearestBeacon.proximity {
      case .immediate:
         print("immediate")
    
      case .near:
         print("near")
      case .far:
         print("far")
      case .unknown:
         print("unknown")
      @unknown default:
         print("@unknown default")
      }
   }
}

实验:

利用 Mac 当 iBeacon,然后啟动刚刚开发的程式侦测 beacon,我们可利用此难得的机会运动一下,走走路让 iPhone 靠近和远离 Mac,观察 locationManager(_:didRangeBeacons:in:) 裡 CLBeacon 距离的变化。

在APP进入后台侦测 beacon & 显示通知
我们也可以在APP进入后台侦测 beacon,甚至在 App 没启动但进入 beacon 范围时,由 iOS 自动啟动 App,然后触发 locationManager:didEnterRegion:。比方以下例子,我们在 locationManager:didEnterRegion: 触发时显示通知讯息:

func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
   let content = UNMutableNotificationContent()
   content.title = "注意"
   content.subtitle = "小明就在你身邊"
   content.badge = 1
   content.sound = UNNotificationSound.default
   let request = UNNotificationRequest(identifier: "notification", content: content, trigger: nil)
   UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
   if CLLocationManager.isRangingAvailable() {
      locationManager.startRangingBeacons(in: region as! CLBeaconRegion)
   }
}

為了显示通知,记得要先请求使用者的同意。

import UIKit
import UserNotifications
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
   var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge], completionHandler: { granted, error in
      if granted {
      } else {
         print("使用者不同意[图片上传中...(IMG_9120.PNG-dd714c-1695030587401-0)]
,哭哭!")
      }
   })
   return true
}

实验步骤:

安装 App,然后啟动 App 同意权限后,先将它杀掉,维持 App 不啟动的状态。
把 iPhone 收进胸前左边口袋,走到远离 beacon 的天涯海角散心。
从天涯海角出发,朝著心爱的 beacon 方向前进。

一旦进入 beacon 的讯号范围,此时将触发 function locationManager:didEnterRegion:,口袋裡的 iPhone 将传来通知声音,显示著通知讯息,xxx就在你身边。
IMG_9120.PNG

ps:

若你原本就在 beacon 范围裡,locationManager(_:didEnterRegion:) 将不会被触发。因此测试时请记得先远离 beacon。

  1. 根据 Apple 文件的说明,为了在APP进入后台侦测到进入 beacon 范围时启动 App,我们得将 Capabilities 页面 Background Modes 的 Location Updates 打开。不过根据实测的结果,不需打开 Location Updates 一样可触发 locationManager:didEnterRegion: & application(_:didFinishLaunchingWithOptions:)。
    Handling location updates in the background
    image.png

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