The Core Bluetooth Mock library was designed to emulate Core Bluetooth objects, providing easy way to test Bluetooth-enabled apps. As the native Bluetooth API is not supported on a simulator, using this library you can run, test and take screenshots of such apps without the need of a physical phone or tablet. You may also start working on the iOS app when your peripheral is still under development.
The Core Bluetooth framework provides the classes needed for your apps to communicate with Bluetooth-equipped low energy (LE) wireless technology. It requires an iPhone or iPad to work making Bluetooth-enabled apps difficult to test. As the documentation states:
Don’t subclass any of the classes of the Core Bluetooth framework. Overriding these classes isn’t supported and results in undefined behavior.
The Core Bluetooth Mock library defines number of CBM... classes and constants, that wrap or imitate the corresponding
CB... counterparts from Core Bluetooth framework. For example,
CBMCentralManager has the same API and
CBCentralManager, etc. On physical iDevices all calls to
forwarded to their native equivalents, but on a simulator a user defined mock implementation is used.
The Core Bluetooth Mock library is available only in Swift, and compatible with iOS 9.0+, macOS 10.13+, tvOS 9.0+ and watchOS 2.0+, with some features available only on newer platforms. For projects running Objective-C we recommend https://github.com/Rightpoint/RZBluetooth library.
Create/Update your Podfile with the following contents
target 'YourAppTargetName' do pod 'CoreBluetoothMock' end
Open the newly created
Create a new Cartfile in your project's root with the following contents
github "https://github.com/NordicSemiconductor/IOS-CoreBluetooth-Mock" ~> x.y // Replace x.y with your required version
Build with carthage
carthage update --platform iOS // also supported are tvOS, watchOS and macOS
Copy the CoreBluetoothMock.framework from Carthage/Build to your project and follow instructions from Carthage.
The library can also be included as SPM package. Simply add it in Xcode: File -> Swift Packages -> Add package dependency, type https://github.com/NordicSemiconductor/IOS-CoreBluetooth-Mock.git and set required version, branch or commit.
If you have Swift.package file, inculde the following dependency:
dependencies: [ // [...] .package(name: "CoreBluetoothMock", url: "https://github.com/NordicSemiconductor/IOS-CoreBluetooth-Mock.git", .upToNextMajor(from: "x.y")) // Replace x.y with your required version ]
and add it to your target:
targets: [ // [...] .target( name: "<Your target name>", dependencies: ["CoreBluetoothMock"]), ]
With this complete, you need to choose one of the following approaches:
Copy CoreBluetoothTypeAliases.swift file to your project. It will create
number of type aliases for all CBM... names and rename them to CB..., so you will not need to perform any changes in
your code, except from removing
import CoreBluetooth in all your files, as the types are now defined locally.
import CoreBluetooth with
import CoreBluetoothMock in your classes.
Replace all instances of CB... with CBM....
The only difference is how the central manager is instantiated. Instead of:
let manager = CBCentralManager(delegate: self, queue: ...)
you need to use the
let manager = CBCentralManagerFactory.initiate(delegate: self, queue: ...)
The last parameter,
forceMock, when set to true, allows to run mock implementation also on a physical device.
CBMPeripheralis a protocols, not a class. That means that, e.g. they cannot be used as
Hashable. When using in maps, you may need to use the identifier. Also, KVO are not yet supported.
When the app using Core Bluetooth Mock library is started on a simulator, or the
forceMock parameter is set to true during
instantiating a central manager instance, a mock version of central manager will be created. Use the following methods and
properties to simulate central manager behavior:
CBMCentralManagerMock.simulateInitialState(_ state: CBMManagerState) - this method should be called before
any central manager instance was created. It defines the intial state of the mock central manager. By default, the manager is powered off.
CBMCentralManager.simulatePowerOn() - turns on the mock central manager.
CBMCentralManager.simulatePowerOff() - turns off the mock central manager. All scans and connections will be terminated.
CBMCentralManagerMock.simulatePeripherals(_ peripherals: [CBMPeripheralSpec]) - defines list of
mock peripheral. This method should be called when the manager is powered off, or before any central manager was initialized.
CBMCentralManagerMock.tearDownSimulation() - sets the state of all currently existing central managers to
clears the list of managers and peripherals bringing the mock manager to initial state.
See AppDelegate.swift for reference. In the sample app the mock implementation is
used only in UI Tests, which lauch the app with
mocking-enabled parameter (see here),
but can be easily modified to use it every time it is launched on a simulator or a device.
CBMPeripheralSpec.Builder - use the builder to define your mock peripheral. Specify the proximity, whether it is advertising
together with advertising data and advertising interval, is it connectable (or already connected when your app starts), by defining
its services and their behavior. A list of such peripheral specifications needs to be set by calling the
method described above.
CBMPeripheralSpec.simulateConnection() - simulates a situation when another app on the iDevice connects to this
peripheral. It will stop advertising (unless
advertisingWhenConnected flag was set) and will be available using
CBMPeripheralSpec.simulateDisconnection(withError:) - simulates a connection error.
CBMPeripheralSpec.simulateReset() - simulates device hard reset. The central will notify delegates 4 seconds (supervision timeout)
after the device has been reset.
CBMPeripheralSpec.simulateProximityChange(:) - simulates moving the peripheral close or away from the device.
CBMPeripheralSpec.simulateValueUpdate(:for:) - simulates sending a notification or indication from the device. All subscribed
clients will be notified a connection interval later.
CBMPeripheralSpec.simulateCaching() - simulates caching the device by the iDevice. Caching pairs the device's MAC with a
random identifier (UUID). A device is also cached whenever it is scanned. Caching makes the device available to be retrieved using
CBMPeripheralSpec.simulateMacChange(:) - simulates the device changing its MAC address. The iDevice will not contain any
cached information about the device, as with the new MAC it is considered to be a new device.
See AppDelegate.swift for reference, where 3 mock peripherals are defined: a test blinky device (like in Nordic SDK), an HRM device (GATT behavior not implemented, as the app does not support it), and a Physical Web Beacon, a non-connectable device. The 2 latter will not pop up on in the sample app, as it is scanning with Service UUID filter.
CBMCentralManagerMock.simulateStateRestoration - this closure will be used when you initiate a central manager
CBMCentralManagerOptionRestoreIdentifierKey option. The map returned will be passed to
centralManager(:willRestoreState:) callback in central manager's delegate.
CBMCentralManagerMock.simulateFeaturesSupport - this closure will be used to emulate Bluetooth features supported
by the manager. It is availalbe on iOS 13+, tvOS 13+ or watchOS 6+.
CBMCentralManagerMock.simulateAuthorization(:) - Simulates the current authorization state of a Core Bluetooth manager.
When any value other than
.allowedAlways is returned, the
CBMCentralManager will change state to
nRF Blinky is an example app targeted towards newcomer BLE developers, and also demonstrating the use of Core Bluetooth Mock library. This application controls an LED on an nRF5DK and receive notifications whenever the button on the kit is pressed and released.
A simplified proprietary service by Nordic Semiconductor, containing two characteristics one to control LED3 and Button1:
1=> LED On
0=> LED Off
1=> Button Pressed
0=> Button Released
For full specification, check out documentation.
Prepare your Development kit.
Start Xcode and run build the project against your target iOS Device (Note: BLE is not available in the iOS simulator, so the iOS device is a requirement to test with real hardware).