Blues
Blues 是一个针对 iOS 的 Core Bluetooth 的高层次、面向对象的类型安全包装。
示例用法
Core Bluetooth 本身不提供任何类型安全,通过 "线" 发送裸未类型化的 Data
数据包。
相比之下,Blues 允许用户轻松指定外围设备、服务、特征、描述符以及通过 BLE 协议发送的实际值的具体类型。
让我们看一下如何实现一个简单的类型安全特性,例如 Battery 服务的 Battery Level 特性,该特性在蓝牙低功耗 GATT 规范中指定
电池级别值
电池级别特性被指定为返回 0 到 100(包括)的整数,对应于百分比电量
使用 GATT 读取特性值子过程读取电池级别特性,并返回当前电池电量的百分比,范围从
0%
到100%
;0%
表示电量完全耗尽,100%
表示电量完全充满。
一个表示这种值的 struct
实现可能看起来像这样
public struct BatteryLevel {
public let percentage: UInt8
public init(percentage: UInt8) {
assert(percentage <= 100)
self.percentage = percentage
}
}
extension BatteryLevel: Equatable {
public static func == (lhs: Battery.Level.Value, rhs: Battery.Level.Value) -> Bool {
return lhs.percentage == rhs.percentage
}
}
extension BatteryLevel: Comparable {
public static func < (lhs: Battery.Level.Value, rhs: Battery.Level.Value) -> Bool {
return lhs.percentage < rhs.percentage
}
}
extension BatteryLevel: CustomStringConvertible {
public var description: String {
return "\(self.percentage)%"
}
}
电池电平值转换器
Blues封装了Core Bluetooth,它本身只是发送和接收Data
实例。
我们定义了一个CharacteristicValueTransformer
,以允许Blues在从电池电平特性读取时发挥最大潜力。
import Blues
public struct BatteryLevelTransformer: CharacteristicValueTransformer {
public typealias Value = Battery.Level.Value
private static let codingError = "Expected value within 0 and 100 (inclusive)."
public func transform(data: Data) -> Result<Value, TypedCharacteristicError> {
let expectedLength = 1
guard data.count == expectedLength else {
return .err(.decodingFailed(message: "Expected data of \(expectedLength) bytes, found \(data.count)."))
}
return data.withUnsafeBytes { (buffer: UnsafePointer<UInt8>) in
let percentage = buffer[0]
if percentage <= 100 {
return .ok(Value(percentage: percentage))
} else {
return .err(.decodingFailed(message: Transformer.codingError))
}
}
}
public func transform(value: Value) -> Result<Data, TypedCharacteristicError> {
return .err(.transformNotImplemented)
}
}
鉴于GATT规范将电池电平特性定义为只读,我们未完全实现transform(value:)
,而是直接返回错误。
电池电平特性
现在我们已经有了类型安全的BatteryLevel
类型和匹配的值转换器,是时候编写类型安全的特性来利用它了。
import Blues
public class BatteryLevelCharacteristic: Blues.Characteristic,
DelegatedCharacteristicProtocol,
StringConvertibleCharacteristicProtocol,
TypedCharacteristicProtocol,
TypeIdentifiable
{
public static let typeIdentifier = Identifier(string: "2A19")
public typealias Transformer = BatteryLevelTransformer
public let transformer: Transformer = .init()
open override var name: String? {
return "Battery-Level"
}
public weak var delegate: CharacteristicDelegate? = nil
}
通过提供weak public var delegate: CharacteristicDelegate?
并遵守DelegatedCharacteristicProtocol
,BatteryLevelCharacteristic
将自动将所有相关方法调用转发到其delegate
。
电池服务
Battery Level
特性(即BatteryLevelCharacteristic
)由GATT指定为电池服务(即BatteryService
)的一部分。
import Blues
public class BatteryService: Blues.Service,
DelegatedServiceProtocol,
TypeIdentifiable
{
public static let typeIdentifier = Identifier(string: "180F")
weak public var delegate: ServiceDelegate?
open var automaticallyDiscoveredCharacteristics: [Identifier]? {
return [
BatteryLevelCharacteristic.typeIdentifier
]
}
}
extension BatteryService: ServiceDataSource {
public func characteristic(with identifier: Identifier, for service: Service) -> Characteristic {
switch identifier {
case BatteryLevelCharacteristic.typeIdentifier:
return BatteryLevelCharacteristic(identifier: identifier, service: service)
default:
return DefaultCharacteristic(identifier: identifier, service: service)
}
}
}
就像我们对BatteryLevelCharacteristic
所做的一样,我们通过遵守相应的DelegatedServiceProtocol
来启用BatteryService
的自动调用代理。
我们覆盖了var automaticallyDiscoveredCharacteristics: [Identifier]?
以启用对BatteryLevelCharacteristic
特性的自动发现。
实现characteristic(with:for:)
允许我们建立一个类型感知的服务和特性层次结构。
电池感知的外设
接下来,我们需要使我们的外围设备类感知到BatteryService
,这通过实现ServiceDataSource
来完成。
open class BatteryAwarePeripheral: Blues.Peripheral,
DelegatedPeripheralProtocol,
DataSourcedPeripheralProtocol
{
public weak var delegate: PeripheralDelegate?
open var automaticallyDiscoveredServices: [Identifier]? {
return [
BatteryService.typeIdentifier
]
}
}
extension BatteryAwarePeripheral: ServiceDataSource {
public func service(with identifier: Identifier, for peripheral: Peripheral) -> Service {
switch identifier {
case BatteryService.typeIdentifier:
return BatteryService(identifier: identifier, peripheral: peripheral)
default:
return DefaultService(identifier: identifier, peripheral: peripheral)
}
}
}
与BatteryService
所做的一样,我们覆盖了var automaticallyDiscoveredServices: [Identifier]?
以启用对BatteryService
服务的自动发现。
中央管理器
最后但同样重要的是,我们需要为我们的 CentralManager
提供数据源
public class InsoleCentralManagerDataSource: CentralManagerDataSource {
public func peripheral(
with identifier: Identifier,
advertisement: Advertisement?,
for manager: CentralManager
) -> Peripheral {
return BatteryAwarePeripheral(identifier: identifier, centralManager: manager)
}
}
安装
向您的项目添加 Blues 的推荐方法是使用 Carthage
github 'regexident/Blues'
或者使用 CocoaPods 将 Blues 添加到您的项目
pod 'Blues'
许可证
Blues 使用的是 MPL-2许可证。详细信息请参见 LICENSE
文件。