rozd/faketooth
**Simulate Bluetooth Low Energy devices on Apple platforms — no hardware required.**
Installation
Add the following dependency to your Package.swift:
dependencies: [
.package(url: "https://github.com/rozd/faketooth.git", from: "0.5.0")
]Usage
1. Import
import Faketooth2. Configure simulated peripherals
Assign an array of FaketoothPeripheral instances to CBCentralManager.simulatedPeripherals. Each peripheral represents a virtual BLE device with its own identifier, name, services, and advertisement data.
CBCentralManager.simulatedPeripherals = [
FaketoothPeripheral(
identifier: UUID(),
name: "Test",
services: [
FaketoothService(
uuid: CBUUID(),
isPrimary: true,
characteristics: [
FaketoothCharacteristic(
uuid: CBUUID(),
properties: [.read, .notify, .write],
descriptors: [
FaketoothDescriptor(
uuid: CBUUID(string: "2902"),
valueProducer: { () -> Any? in
return Data(capacity: 2)
}
)
],
valueProducer: { "Hello".data(using: .utf8) },
valueHandler: { data in
print("\(String(data: data!, encoding: .utf8)!)")
}
)
]
)
],
advertisementData: [
CBAdvertisementDataLocalNameKey: "Name for Advertisement"
]
)
]3. Use CoreBluetooth as usual
Build and run your project. Faketooth intercepts CBCentralManager calls transparently — scanning, connecting, service discovery, reads, writes, and notifications all work against your virtual peripherals.
When simulation is active, Faketooth:
- Fires
centralManagerDidUpdateState:with.poweredOnon first interaction - Filters
scanForPeripherals(withServices:)results by advertised service UUIDs - Filters
discoverServices:anddiscoverCharacteristics:forService:by UUID when non-nil - Supports
retrieveConnectedPeripherals(withServices:)for finding connected simulated peripherals
4. Simulate errors
Set optional error properties to test your app's error-handling code paths. When an error is set, the corresponding delegate callback fires with the error and the operation does not mutate state (matching real CoreBluetooth behavior).
// Simulate a connection failure
peripheral.connectionError = NSError(
domain: CBErrorDomain,
code: CBError.connectionFailed.rawValue,
userInfo: nil
)
// centralManager(_:didFailToConnect:error:) will be called instead of didConnect
// Simulate a characteristic read error
characteristic.readError = NSError(
domain: CBATTErrorDomain,
code: CBATTError.readNotPermitted.rawValue,
userInfo: nil
)
// peripheral(_:didUpdateValueFor:error:) will receive the error
// Clear the error to restore normal behavior
peripheral.connectionError = nil
characteristic.readError = nilAvailable error properties:
FaketoothPeripheral:connectionError,disconnectionError,discoverServicesError,discoverCharacteristicsError,discoverDescriptorsErrorFaketoothCharacteristic:readError,writeErrorFaketoothDescriptor:readError,writeError
5. Deactivate
Set simulatedPeripherals to nil to restore real CoreBluetooth behavior:
CBCentralManager.simulatedPeripherals = nilHow It Works
Faketooth swizzles eight CBCentralManager methods in +load. Each swizzled method checks whether simulation is active — if simulatedPeripherals is nil, it falls through to the original CoreBluetooth implementation. This means Faketooth is completely inert unless explicitly activated.
| Class | Role | |---|---| | CBCentralManager+Faketooth | Swizzles scanning, connecting, retrieval, state, isScanning | | FaketoothPeripheral | Overrides service discovery, read/write, notifications; supports error injection | | FaketoothService | Holds characteristics, links back to peripheral | | FaketoothCharacteristic | Supports valueProducer (read), valueHandler (write), and error injection | | FaketoothDescriptor | Supports valueProducer, valueHandler, and error injection | | FaketoothSettings | Configurable delays per operation via FaketoothDelaySettings |
Examples
The repository includes examples demonstrating different use cases. See the Demo directory.
Contributing
Contributions are welcome! If you encounter issues, have questions, or want to suggest improvements, please open an issue.
License
Faketooth is available under the MIT license. See the LICENSE file for more information.
Package Metadata
Repository: rozd/faketooth
Default branch: master
README: README.md