Skip to content

rozd/faketooth

Repository files navigation

Faketooth

Platforms Swift 5.2+ Release codecov License

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

Faketooth uses Objective-C runtime swizzling to replace CBCentralManager and CBPeripheral methods with simulated implementations, letting you create virtual peripherals with custom services, characteristics, descriptors, and advertisement data. Your existing CoreBluetooth code works unchanged — just set simulatedPeripherals and go.


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

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).

// 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:

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

About

BLE simulation for iOS platform

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors