功能
- A futures 接口,用于替换协议实现。
- 对
Peripheral
连接、Service
扫描和Service
+Characteristic
发现以及Characteristic
读写设定超时。 - 一个用于指定 GATT 配置文件的 DSL。
- 封装序列化和反序列化的特性配置文件类型。
- 示例 应用实现了 CentralManager 和 PeripheralManager。
- 在 App Store 中可获得完全功能并可扩展的扫描器和周边模拟器。
- 线程安全。
- 综合测试覆盖率。
要求
- iOS 10.0+
- Xcode 9.3
安装
CocoaPods
CocoaPods 是一个 Xcode 依赖管理器。可以使用以下命令安装,
gem install cocoapods
需要 CocoaPods 1.1+,
将 BluCapKit
添加到您的 Podfile
中,
platform :ios, '10.0'
use_frameworks!
target 'Your Target Name' do
pod 'BlueCapKit', '~> 0.7'
end
要启用 DBUG
输出,请将以下 post_install
脚本钩子 添加到您的 Podfile
中。
Carthage
Carthage 是 Xcode 项目的分布式依赖管理器。可以使用 Homebrew 安装,
brew update
brew install carthage
要将 BlueCapKit
添加到您的 Cartfile
中,
github "troystribling/BlueCap" ~> 0.7
运行以下命令以下载和构建 BlueCapKit.framework
,
carthage update
然后将其添加到您的项目中。
如果需要,使用 --no-build
选项,
carthage update --no-build
这将只下载 BlueCapKit
。然后按照 手动 中的步骤将其添加到项目中。
Manual
- 将 BlueCap 放置在您的项目目录中的某个位置。您可以选择复制它或将它添加为 git 子模块。
- 打开 BlueCap 项目文件夹,将 BlueCapKit.xcodeproj 拖拽到您应用程序 Xcode 项目的项目导航器中。
- 在您的 Projects Info 选项卡中设置 iOS Deployment Target 为 9.0,并验证 BlueCapKit.xcodeproj 的 iOS Deployment Target 也设置为 9.0。
- 在您的项目目标的 General 选项卡中,将顶部 BlueCapKit.framework 添加为 Embedded Binary。
- 在 Build Phases 选项卡中,将 BlueCapKit.framework 添加为 Target Dependency,并在 Link Binary With Libraries 中添加 CoreLocation.framework 和 CoreBluetooth.framework。
入门
使用BlueCap可以轻松实现CentralManager
和PeripheralManager
应用程序,序列化和反序列化与蓝牙设备交换的消息,并定义可重用的GATT配置文件定义。BlueCap异步接口使用Futures而不是常规的阻塞接口或协议委托模式。可以将Futures逐个连接,将上一个Future的结果作为下一个Future的输入。这简化了应用程序的实现,因为消除了异步调用之间的状态持久性,并且代码不会分布在多个文件中,这在协议委托模式中是常见的,也不会深度嵌套,这在阻塞接口中是常见的。在本节中,将简要介绍应用程序的构建方法。以下章节将描述支持的使用案例。示例应用程序也可用。
CentralManager
将描述一个简单的CentralManager实现,该实现可扫描提供TiSensorTag加速度计服务的广告外围设备,在外围设备发现时连接,发现服务和特性并订阅加速度计数据更新。
所有应用程序都从调用CentralManager#whenStateChanges
开始。
let manager = CentralManager(options: [CBCentralManagerOptionRestoreIdentifierKey : "us.gnos.BlueCap.central-manager-documentation" as NSString])
let stateChangeFuture = manager.whenStateChanges()
要开始扫描提供TiSensorTag加速度计服务的广告Peripherals
,在whenStateChanges()
后跟CentralManager#startScanning
,并使用SimpleFutures的FutureStream#flatMap
组合器将这两个结合起来。还定义了一个应用程序错误对象,
public enum AppError : Error {
case invalidState
case resetting
case poweredOff
case unknown
case unlikely
}
let serviceUUID = CBUUID(string: TISensorTag.AccelerometerService.uuid)
let scanFuture = stateChangeFuture.flatMap { [weak manager] state -> FutureStream<Peripheral> in
guard let manager = manager else {
throw AppError.unlikely
}
switch state {
case .poweredOn:
return manager.startScanning(forServiceUUIDs: [serviceUUID])
case .poweredOff:
throw AppError.poweredOff
case .unauthorized, .unsupported:
throw AppError.invalidState
case .resetting:
throw AppError.resetting
case .unknown:
throw AppError.unknown
}
}
scanFuture.onFailure { [weak manager] error in
guard let appError = error as? AppError else {
return
}
switch appError {
case .invalidState:
break
case .resetting:
manager?.reset()
case .poweredOff:
break
case .unknown:
break
}
}
在此处,当收到.poweredOn
时,开始扫描。在所有其他状态更改时,适当的错误被thrown
,并在错误处理程序中处理。
要连接发现的周边设备,在扫描后跟随Peripheral#connect
,并使用FutureStream#flatMap
结合起来,
var peripheral: Peripheral?
let connectionFuture = scanFuture.flatMap { [weak manager] discoveredPeripheral -> FutureStream<Void> in
manager?.stopScanning()
peripheral = discoveredPeripheral
return peripheral.connect(connectionTimeout: 10.0)
}
在此处,在发现具有所需服务UUID的外围设备后也会停止扫描。
Peripheral
的Lervices
和Characteristics
需要被发现,并且需要处理连接错误。Characteristic
发现是通过'Peripheral#discoverServices'和'Service#discoverCharacteristics'执行的,并在'AppError'中添加了更多错误。
public enum AppError : Error {
case dataCharactertisticNotFound
case enabledCharactertisticNotFound
case updateCharactertisticNotFound
case serviceNotFound
case invalidState
case resetting
case poweredOff
case unknown
case unlikely
}
let discoveryFuture = connectionFuture.flatMap { [weak peripheral] () -> Future<Void> in
guard let peripheral = peripheral else {
throw AppError.unlikely
}
return peripheral.discoverServices([serviceUUID])
}.flatMap { [weak peripheral] () -> Future<Void> in
guard let peripheral = peripheral, let service = peripheral.services(withUUID: serviceUUID)?.first else {
throw AppError.serviceNotFound
}
return service.discoverCharacteristics([dataUUID, enabledUUID, updatePeriodUUID])
}
discoveryFuture.onFailure { [weak peripheral] error in
switch error {
case PeripheralError.disconnected:
peripheral?.reconnect()
case AppError.serviceNotFound:
break
default:
break
}
}
在此处,如果外围设备断开连接,则尝试重新连接,并处理AppError.serviceNotFound
错误。最后,读取并订阅数据Characteristic
并处理dataCharacteristicsNotFound
。
public enum AppError : Error {
case dataCharactertisticNotFound
case enabledCharactertisticNotFound
case updateCharactertisticNotFound
case serviceNotFound
case invalidState
case resetting
case poweredOff
case unknown
}
var accelerometerDataCharacteristic: Characteristic?
let subscriptionFuture = discoveryFuture.flatMap { [weak peripheral] () -> Future<Void> in
guard let peripheral = peripheral, let service = peripheral.services(withUUID: serviceUUID)?.first else {
throw AppError.serviceNotFound
}
guard let dataCharacteristic = service.service.characteristics(withUUID: dataUUID)?.first else {
throw AppError.dataCharactertisticNotFound
}
accelerometerDataCharacteristic = dataCharacteristic
return dataCharacteristic.read(timeout: 10.0)
}.flatMap { [weak accelerometerDataCharacteristic] () -> Future<Void> in
guard let accelerometerDataCharacteristic = accelerometerDataCharacteristic else {
throw AppError.dataCharactertisticNotFound
}
return accelerometerDataCharacteristic.startNotifying()
}.flatMap { [weak accelerometerDataCharacteristic] () -> FutureStream<Data?> in
guard let accelerometerDataCharacteristic = accelerometerDataCharacteristic else {
throw AppError.dataCharactertisticNotFound
}
return accelerometerDataCharacteristic.receiveNotificationUpdates(capacity: 10)
}
dataUpdateFuture.onFailure { [weak peripheral] error in
switch error {
case PeripheralError.disconnected:
peripheral?.reconnect()
case AppError.serviceNotFound:
break
case AppError.dataCharactertisticNotFound:
break
default:
break
}
}
这些示例可以编写为单个flatMap
链,如CentralManager示例中所示。
PeripheralManager
将描述一个简单的PeripheralManager
应用程序,该应用程序模拟一个支持所有Characteristics
的TiSensorTag加速度计服务。它将广播服务并对可写Characteristics
上的特性写入请求做出响应。
首先创建Characteristics
和Service
,然后将Characteristics
添加到Service
中。
// create accelerometer service
let accelerometerService = MutableService(uuid: TISensorTag.AccelerometerService.uuid)
// create accelerometer data characteristic
let accelerometerDataCharacteristic = MutableCharacteristic(profile: RawArrayCharacteristicProfile<TISensorTag.AccelerometerService.Data>())
// create accelerometer enabled characteristic
let accelerometerEnabledCharacteristic = MutableCharacteristic(profile: RawCharacteristicProfile<TISensorTag.AccelerometerService.Enabled>())
// create accelerometer update period characteristic
let accelerometerUpdatePeriodCharacteristic = MutableCharacteristic(profile: RawCharacteristicProfile<TISensorTag.AccelerometerService.UpdatePeriod>())
// add characteristics to service
accelerometerService.characteristics = [accelerometerDataCharacteristic, accelerometerEnabledCharacteristic, accelerometerUpdatePeriodCharacteristic]
接下来创建PeripheralManager
,添加Service
并开始广播。
enum AppError: Error {
case invalidState
case resetting
case poweredOff
case unsupported
case unlikely
}
let manager = PeripheralManager(options: [CBPeripheralManagerOptionRestoreIdentifierKey : "us.gnos.BlueCap.peripheral-manager-documentation" as NSString])
let startAdvertiseFuture = manager.whenStateChanges().flatMap { [weak manager] state -> Future<Void> in
guard let manager = manager else {
throw AppError.unlikely
}
switch state {
case .poweredOn:
manager.removeAllServices()
return manager.add(self.accelerometerService)
case .poweredOff:
throw AppError.poweredOff
case .unauthorized, .unknown:
throw AppError.invalidState
case .unsupported:
throw AppError.unsupported
case .resetting:
throw AppError.resetting
}
}.flatMap { [weak manager] _ -> Future<Void> in
guard let manager = manager else {
throw AppError.unlikely
}
manager.startAdvertising(TISensorTag.AccelerometerService.name, uuids:[CBUUID(string: TISensorTag.AccelerometerService.uuid)])
}
startAdvertiseFuture.onFailure { [weak manager] error in
switch error {
case AppError.poweredOff:
manager?.reset()
case AppError.resetting:
manager?.reset()
default:
break
}
manager?.stopAdvertising()
}
现在响应对 accelerometerEnabledFuture
和 accelerometerUpdatePeriodFuture
的写事件。
// respond to Update Period write requests
let accelerometerUpdatePeriodFuture = startAdvertiseFuture.flatMap {
accelerometerUpdatePeriodCharacteristic.startRespondingToWriteRequests()
}
accelerometerUpdatePeriodFuture.onSuccess { [weak accelerometerUpdatePeriodCharacteristic] (request, _) in
guard let accelerometerUpdatePeriodCharacteristic = accelerometerUpdatePeriodCharacteristic else {
throw AppError.unlikely
}
guard let value = request.value, value.count > 0 && value.count <= 8 else {
return
}
accelerometerUpdatePeriodCharacteristic.value = value
accelerometerUpdatePeriodCharacteristic.respondToRequest(request, withResult:CBATTError.success)
}
// respond to Enabled write requests
let accelerometerEnabledFuture = startAdvertiseFuture.flatMap {
accelerometerEnabledCharacteristic.startRespondingToWriteRequests(capacity: 2)
}
accelerometerEnabledFuture.onSuccess { [weak accelerometerUpdatePeriodCharacteristic] (request, _) in
guard let accelerometerEnabledCharacteristic = accelerometerEnabledCharacteristic else {
throw AppError.unlikely
}
guard let value = request.value, value.count == 1 else {
return
}
accelerometerEnabledCharacteristic.value = request.value
accelerometerEnabledCharacteristic.respondToRequest(request, withResult:CBATTError.success)
}
详情请见 外设管理器示例。
测试用例
测试用例可用。要运行,请输入:
pod install
并从生成的 workspace
中的测试选项卡中运行。
示例
有可用的示例,这些示例实现了中央管理器和外设管理器。还有 BluCap 应用程序。示例项目使用 CocoaPods 或 Carthage 构建。CocoaPods 项目需要在构建之前安装 Pod,
pod install
Carthage 项目需要:
carthage update
BlueCap | BlueCap 提供中央管理器、外设管理器和 iBeacon 范围的 GATT 配置文件实现。在中央管理器模式下,提供用于 Bluetooth LE 外设的扫描器。在外设管理器模式下,支持模拟任何包含的 GATT 配置文件或 iBeacon。在 iBeacon 范围模式下,可以配置和监控信标区域。 |
中央管理器 | 中央管理器实现 BLE 中央管理器,扫描广告 TiSensorTag 加速度计服务的服务。当发现外设时,将建立连接,发现服务,启用加速度计,并且应用程序订阅加速度计数据更新。还可以更改数据更新周期。 |
带有配置文件的中央管理器 | 使用 GATT 配置文件定义创建服务的中央管理器版本。 |
外设管理器 | 外设管理器实现 BLE 外设管理器,广告 TiSensorTag 加速度计服务。外设管理器使用板载加速度计提供数据更新。 |
带有配置文件的外设管理器 | 使用 GATT 配置文件定义创建服务的外设版本。 |
信标 | 模拟 iBeacon 的外设。 |
信标 | iBeacon 范围 |
文档
BlueCap 支持许多简化蓝牙低功耗应用程序编写的功能。以下各节将描述具有示例实现的用例。
-
CentralManager:BlueCap CentralManager实现通过CBCentralManagerDelegate和CBPeripheralDelegate协议实现替换为使用SimpleFutures的Scala Future接口。
-
PeripheralManager:BlueCap PeripheralManager实现通过CBPeripheralManagerDelegate协议实现替换为使用SimpleFutures的Scala Future接口。
-
序列化/反序列化:设备消息的序列化和反序列化。
-
GATT配置文件定义:定义可重用的GATT配置文件并将配置文件添加到BlueCap应用中。