使用现代编程范式连接 Bluetooth 设备并进行通信。
Spezi Bluetooth 模块提供了一种便捷的方式来处理 Bluetooth 设备的 State 管理,从不同的服务和特征检索数据,以及向服务和特征的组合写入数据。
此软件包在底层使用了 Apple 的 CoreBluetooth 框架。
注意
您需要对 Bluetooth 术语和底层软件模型有一定的了解,才能理解 Spezi Bluetooth 模块的结构和 API。 您可以在Wikipedia Bluetooth Low Energy (LE) 软件模型部分或蓝牙技术开发者指南中找到一个不错的概述。
您需要将 Spezi Bluetooth Swift 软件包添加到 Xcode 中的应用或Swift 软件包中。
重要
如果您的应用程序尚未配置为使用 Spezi,请按照Spezi 设置文章设置核心 Spezi 基础设施。
需要在基于 Spezi 的应用程序中使用 configuration
在 SpeziAppDelegate
中注册 Bluetooth
模块。
class ExampleAppDelegate: SpeziAppDelegate {
override var configuration: Configuration {
Configuration {
Bluetooth {
// discover devices ...
}
}
}
}
注意
Bluetooth
模块允许使用 BluetoothDevice
实现和属性包装器(如 Service
和 Characteristic
)来声明性地定义 Bluetooth 设备。
以下代码示例演示了如何实现自己的 Bluetooth 设备。
首先,我们通过实现 BluetoothService
来定义我们的 Bluetooth 服务。 我们使用 Characteristic
属性包装器来声明其特性。 请注意,值类型必须是可选的,并分别符合 ByteEncodable
、ByteDecodable
或 ByteCodable
。
struct DeviceInformationService: BluetoothService {
static let id: BTUUID = "180A"
@Characteristic(id: "2A29")
var manufacturer: String?
@Characteristic(id: "2A26")
var firmwareRevision: String?
}
我们现在可以在 MyDevice
实现中使用此 Bluetooth 服务,如下所示。
提示
我们使用 DeviceState
和 DeviceAction
属性包装器来访问设备状态及其操作。 这两个属性包装器也可以在 BluetoothService
类型中使用。
class MyDevice: BluetoothDevice {
@DeviceState(\.id)
var id: UUID
@DeviceState(\.name)
var name: String?
@DeviceState(\.state)
var state: PeripheralState
@Service var deviceInformation = DeviceInformationService()
@DeviceAction(\.connect)
var connect
@DeviceAction(\.disconnect)
var disconnect
required init() {}
}
我们使用上述 BluetoothDevice
实现来配置 Bluetooth
模块在 SpeziAppDelegate 中。
import Spezi
class ExampleDelegate: SpeziAppDelegate {
override var configuration: Configuration {
Configuration {
Bluetooth {
// Define which devices type to discover by what criteria .
// In this case we search for some custom FFF0 service that is advertised.
Discover(MyDevice.self, by: .advertisedService("FFF0"))
}
}
}
}
在您的 Spezi 应用中配置了 Bluetooth
模块后,您可以在 Environment
中访问该模块。
您可以使用 scanNearbyDevices(enabled:with:minimumRSSI:advertisementStaleInterval:autoConnect:)
和 autoConnect(enabled:with:minimumRSSI:advertisementStaleInterval:)
修饰符来扫描附近的设备和/或自动连接到第一个可用设备。 否则,您也可以使用 scanNearbyDevices(minimumRSSI:advertisementStaleInterval:autoConnect:)
和 stopScanning()
手动启动和停止扫描附近的设备。
要检索附近设备的列表,您可以使用 nearbyDevices(for:)
。
提示
要轻松访问第一个连接的设备,您可以从 SwiftUI Environment 查询您的 BluetoothDevice
类型。 请务必使用相应的 Environment(_:)
初始化程序将属性声明为可选。
以下代码示例演示了从环境中检索 Bluetooth
模块、列出所有附近设备、自动连接到第一个设备以及显示当前连接设备的一些基本信息的所有这些步骤。
import SpeziBluetooth
import SwiftUI
struct MyView: View {
@Environment(Bluetooth.self)
var bluetooth
@Environment(MyDevice.self)
var myDevice: MyDevice?
var body: some View {
List {
if let myDevice {
Section {
Text("Device")
Spacer()
Text("\(myDevice.state.description)")
}
}
Section {
ForEach(bluetooth.nearbyDevices(for: MyDevice.self), id: \.id) { device in
Text("\(device.name ?? "unknown")")
}
} header: {
HStack {
Text("Devices")
.padding(.trailing, 10)
if bluetooth.isScanning {
ProgressView()
}
}
}
}
.scanNearbyDevices(with: bluetooth, autoConnect: true)
}
}
提示
使用 ConnectedDevices
从 SwiftUI 环境中检索已连接设备的完整列表。
上一节介绍了如何发现附近的设备以及从环境中检索当前连接的设备。 这对于与当前附近的设备建立临时的连接非常有用。 但是,如果您想连接到特定的、以前配对的设备,这可能不是最有效的方法。 在这种情况下,您可以使用 retrieveDevice(for:as:)
方法来检索已知的设备。
以下是一个简短的代码示例,说明了此方法。
let id: UUID = ... // a Bluetooth peripheral identifier (e.g., previously retrieved when pairing the device)
let device = bluetooth.retrieveDevice(for: id, as: MyDevice.self)
await device.connect() // assume declaration of @DeviceAction(\.connect)
// Connect doesn't time out. Connection with the device will be established as soon as the device is in reach.
Spezi Module
是一种将您的应用程序构建到不同子系统中的好方法,并提供了广泛的功能来建模模块之间的关系和依赖性。 每个 BluetoothDevice
都是一个 Module
。 因此,您可以使用标准的 模块依赖项基础设施,从任何 Spezi Module
中轻松访问您的 SpeziBluetooth 设备。 同时,每个 BluetoothDevice
都可以受益于与其他 Spezi Module
相同的功能。
以下是一个简短的代码示例,演示了 BluetoothDevice
如何使用 @Dependency
属性来与 Spezi 应用程序中配置的 Spezi Module 交互。
class Measurements: Module, EnvironmentAccessible, DefaultInitializable {
required init() {}
func recordNewMeasurement(_ measurement: WeightMeasurement) {
// ... process measurement
}
}
class MyDevice: BluetoothDevice {
@Service var weightScale = WeightScaleService()
// declare dependency to a configured Spezi Module
@Dependency var measurements: Measurements
required init() {}
func configure() {
weightScale.$weightMeasurement.onChange { [weak self] value in
self?.handleNewMeasurement(value)
}
}
private func handleNewMeasurement(_ measurement: WeightMeasurement) {
measurements.recordNewMeasurement(measurement)
}
}
有关更多信息,请参阅 API 文档。
欢迎对此项目进行贡献。请务必先阅读贡献指南和贡献者盟约行为准则。
此项目已获得 MIT 许可证的许可。 有关更多信息,请参阅 许可证。