---
title: rozd/faketooth
framework: Swift Package Catalog
role: article
path: packages/rozd/faketooth
---

# rozd/faketooth

**Simulate Bluetooth Low Energy devices on Apple platforms — no hardware required.**

## Installation

Add the following dependency to your `Package.swift`:

```swift dependencies: [     .package(url: "https://github.com/rozd/faketooth.git", from: "0.5.0") ] ```

## Usage

### 1. Import

```swift import Faketooth ```

### 2. 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.

```swift 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 `.poweredOn` on first interaction - Filters `scanForPeripherals(withServices:)` results by advertised service UUIDs - Filters `discoverServices:` and `discoverCharacteristics: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).

```swift // 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 = nil ```

Available error properties: - **`FaketoothPeripheral`**: `connectionError`, `disconnectionError`, `discoverServicesError`, `discoverCharacteristicsError`, `discoverDescriptorsError` - **`FaketoothCharacteristic`**: `readError`, `writeError` - **`FaketoothDescriptor`**: `readError`, `writeError`

### 5. Deactivate

Set `simulatedPeripherals` to `nil` to restore real CoreBluetooth behavior:

```swift CBCentralManager.simulatedPeripherals = nil ```

## How 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](https://github.com/rozd/faketooth/tree/master/Demo) directory.

## Contributing

Contributions are welcome! If you encounter issues, have questions, or want to suggest improvements, please [open an issue](https://github.com/rozd/faketooth/issues).

## License

Faketooth is available under the MIT license. See the [LICENSE](LICENSE) file for more information.

## Package Metadata

Repository: rozd/faketooth

Default branch: master

README: README.md
