exphat/swiftbluetooth
Easily interface with Bluetooth peripherals in new or existing projects through modern async Swift API's.
Features
- [x] Parity with existing
CoreBluetoothAPIs for easy, incremental migration of existing projects - [x] Modern, async-await API for discovery, connection, read/write, and more
- [x] Alternate callback-based API for codebases not using Swift Concurrency
- [x] Subscribe to peripheral discoveries, value updates, and more through
AsyncStream - [x] Easy await-ing of
CentralManagerstate - [x] Staticly typed characteristics
- [x] Thread safe
- [x] Zero inherited dependencies
- [x] Tested with included
SwiftBluetoothMocklibrary
Examples
Complete example
Async API's make the entire Bluetooth lifecycle much simpler, using method names you're already familiar with from CoreBluetooth.
import SwiftBluetooth
let central = CentralManager()
try await central.waitUntilReady()
// Find and connect to the first available peripheral
let peripheral = await central.scanForPeripherals(withServices: [myService]).first!
try await central.connect(peripheral, timeout: connectionTimeout)
// Discover services and characteristics
let service = try await peripheral.discoverServices([myService]).first!
let _ = try await peripheral.discoverCharacteristics([.someCharacteristic], for: service)
// Read data directly from your characteristic
let value = try await peripheral.readValue(for: .someCharacteristic)
central.cancelPeripheralConnection(peripheral)Callbacks
Stock CoreBluetooth methods now also have an additional overload that takes a completionHandler for projects not using Swift Concurrency.
central.connect(peripheral) { result in
if result == .failure(let error) {
// Issue connecting
return
}
// Connected!
}Methods often now have 3 overloads. One marked
async, one with acompletionHandler, and the original CoreBluetooth verision. Meaning you can choose whichever is most convienient at the time.
Stream discovered peripherals
Some operations (like scanning) conform to AsyncStream, meaning you can use for-await-in loops to iterate over new items.
for await peripheral in await central.scanForPeripherals() {
print("Discovered:", peripheral.name ?? "Unknown")
}Defining characteristics
Characteristics can be staticly defined on the stock Characteristic type, which removes the burden of keeping track of CBCharacteristic instances around your app.
extension Characteristic {
static let someCharacteristic = Self("00000000-0000-0000-0000-000000000000")
}
// Use those characteristics later on your peripheral
try await myPeripheral.readValue(for: .someCharacteristic)Watching with callbacks
Peristent tasks return a CancellableTask that needs to be cancelled when you're done.
let task = central.scanForPeripherals { peripheral in
print("Discovered:", peripheral.name ?? "Unknown")
}
// At some point later, cancel the task to stop scanning
task.cancel()Note
Calling central.stopScan() will also cancel any peripheral scanning tasks
Migrate existing projects
Existing projects that already use CoreBluetooth can immediately get started by typealiasing the stock types. Afterwards, you can adopt async API's at your own pace.
import CoreBluetooth
import SwiftBluetooth // Add this
// Override existing CoreBluetooth classes to use SwiftBluetooth
typealias CBCentralManager = SwiftBluetooth.CentralManager
typealias CBCentralManagerDelegate = SwiftBluetooth.CentralManagerDelegate
typealias CBPeripheral = SwiftBluetooth.Peripheral
typealias CBPeripheralDelegate = SwiftBluetooth.PeripheralDelegate
// Your existing code should continue to work as normal.
// But now you have access to all the new API's!Install
Xcode
Add https://github.com/exPHAT/SwiftBluetooth.git in the "Swift Package Manager" tab.
Swift Package Manager
Add SwiftBluetooth as a dependency in your Package.swift file:
let package = Package(
...
dependencies: [
// Add the package to your dependencies
.package(url: "https://github.com/exPHAT/SwiftBluetooth.git", branch: "master"),
],
...
targets: [
// Add SwiftBluetooth as a dependency on any target you want to use it in
.target(name: "MyTarget",
dependencies: [.byName(name: "SwiftBluetooth")])
]
...
)Package Metadata
Repository: exphat/swiftbluetooth
Default branch: master
README: README.md