-
Notifications
You must be signed in to change notification settings - Fork 8
Home
This is documentation page of the library, here you can find a brief
explanation of the library's architecture and how to perform the
most important operations. Although full knowledge of the
CoreBluetooth
framework is not required, it is recommend to a least
have an idea of the key components of the framework. Remind that this
library is a wrapper of CoreBluetooth
but some of its classes are
widely used and expose in the library's API.
If this is the first time you are using Bluetooth we recommend you
to read the CoreBluetooth programming guide
and the framework reference.
Also this WWDC 2013 video
is pretty good to get an introduction to CoreBluetooth
.
One of the main goals of this library is to separate the responsibilities of the connection process in different classes in order to have a more maintainable and easy to read code. We think that by providing small classes with concrete responsibilities, the application's code will be easier and simpler to write. The following is a list of the most important classes and protocols in this library:
-
WLXBluetoothDeviceManager
: The root class of the library that provides access to all the other classes. -
WLXDeviceDiscoverer
: A protocol that exposes an API to discover devices with Bluetooth 4.0 capabilities. -
WLXConnectionManager
: A protocol that exposes an API to manage the connection life cycle with a Bluetooth device that was previously discovered. -
WLXServicesManager
: A class that provides a way to discover services and create a manager for each discovered service. -
WLXServiceManager
: A class that manages a Bluetooth service providing an API to interact with it. This is the class that allows clients to read and write values to a characteristic.
The root class that provides access to all the other classes is
WLXBluetoothDeviceManager
. You should never create an instance
of another class of this library except from WLXBluetoothDeviceManager
.
Any other object instance of this library should be obtained from an
instance of WLXBluetoothDeviceManager
.
Although is not necessary, is recommended
to only have one instance of this class in the application's life cycle.
There are several way to get an instance but the easies one is to call the
class method deviceManager
. This method will create a new instance of
WLXBluetoothDeviceManager
using a default dispatch_queue_t
. This means
that all the bluetooth related operation will be executed on dedicated
serial dispatch queue. You can also create an instance of
WLXBluetoothDeviceManager
with your own custom dispatch queue by calling
the class method deviceManagerWithQueye:
. Keep in mind that the use of
serial dispatch queues is recommended because they assure that the
messages will be processed in the same order they arrived and one at
a time. This might be important for your application's logic and is a safe
default.
The discovery process is handled by an object that conforms to the WLXDiscoverer
protocol. You can get this object by accessing the discoverer
property of the
WLXBluetoothDeviceManager
. This property will return an object that implements
the WLXDiscoverer
protocol.
The WLXDiscoverer
protocol exposes a simple API to discover devices. The only
thing you should do to start discovering is invoke the
discoverDevicesNamed:withServices:andTimeout:
method. You can filter devices
by their name,
the services they expose or both. A discovery timeout (in milliseconds) is
mandatory and must be a positive number. After the timeout expires the
discovery process will be stopped. If you want to filter devices by the
services they expose you should provide an NSArray
of CBUUID
objects
with the required services UUID.
You can also configure some properties of the discovery process by
setting the scanOptions
property of the WLXDiscoverer
object. This
property expect an NSDictionary
with some specific values.
The default value is @{CBCentralManagerScanOptionAllowDuplicatesKey: @NO};
Every time a new devices gets discovered the deviceDiscoverer:discoveredDevice:
method of the WLXDeviceDiscovererDelegate
. You should implement this protocol
and set the delegate using the delegate
property of the WLXDiscoverer
object.
When a device is discovered a WLXDiscoveryData
object is provided. This object
contains information about the device and its discovery data, for example
it contains the CBPeripheral
object.
The WLXDeviceDiscovererDelegate
will also be notified every time the
discovery process is started or stopped.
To stop the discovery process you can invoke the stopDiscoveringDevices
method.
The WLXDiscoverer
also publishes discovery related notifications using
NSNotificationCenter
. The following is a list of the notifications:
-
WLXBluetoothDeviceStartDiscovering
: When the discovery process is started. -
WLXBluetoothDeviceStoptDiscovering
: Every time a new devices is discovered. You can get theWLXDiscoveryData
object from the user info dictionary of theNSNotification
object using the keyWLXBluetoothDeviceDiscoveryData
. -
WLXBluetoothDeviceDeviceDiscovered
: When the discovery process is stopped.
To discover all the devices that expose a service with UUID
68753A44-4D6F-1226-9C60-0050E4C00066
with a timeout of 30 seconds, you
should call the discover method with the following parameters:
WLXBluetoothDeviceManager * deviceManager = [WLXBluetoothDeviceManager deviceManager];
id<WLXDiscoverer> discoverer = deviceManager.discoverer;
discoverer.delegate = self;
CBUUID * serviceUUID = [CBUUID UUIDWithString:@"68753A44-4D6F-1226-9C60-0050E4C00066"];
[discoverer discoverDevicesNamed:nil withServices:@[serviceUUID] andTimeout:30000];
If you want to list only the devices whose name starts with Tracker
,
you can pass a regular expression to the name parameter:
[discoverer discoverDevicesNamed:@"Tracker.*" withServices:nil andTimeout:30000];
This will match device with the name Tracker
, Tracker1
, Tracker A
, etc. You can also combine the name filter with the service filter.
Here is an example of UITableViewController
that lists all the discovered
devices in a UITableView
.
@import CoreBluetooth;
@interface WLXDiscoverViewController ()<WLXDeviceDiscovererDelegate>
@property (nonatomic) id<WLXDeviceDiscoverer> discoverer;
@end
static NSUInteger DISCOVERY_TIMEOUT = 30000; //ms
@implementation WLXDiscoverViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)viewWillAppear:(BOOL)animated {
self.discoverer.delegate = self;
[self discover];
}
- (void)viewWillDisappear:(BOOL)animated {
[self.discoverer stopDiscoveringDevices];
}
#pragma mark - WLXDeviceDiscovererDelegate methods
- (void)deviceDiscoverer:(id<WLXDeviceDiscoverer>)discoverer startDiscoveringDevicesWithTimeout:(NSUInteger)timeout {
}
- (void)deviceDiscoverer:(id<WLXDeviceDiscoverer>)discoverer discoveredDevice:(WLXDeviceDiscoveryData *)discoveryData {
[self.tableView performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO];
}
- (void)deviceDiscovererStopDiscoveringDevices:(id<WLXDeviceDiscoverer>)discoverer {
}
#pragma mark - UITableViewDelegate methods
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
CBPeripheral * peripheral = [self peripheralForIndexPath:indexPath];
[self.delegate discoverViewController:self didSelectPeripheral:peripheral];
}
#pragma mark - UITableViewDataSource methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.discoverer.discoveredDevices count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell * cell = [self.tableView dequeueReusableCellWithIdentifier:@"CellIdentifier" forIndexPath:indexPath];
CBPeripheral * peripheral = [self peripheralForIndexPath:indexPath];
cell.textLabel.text = peripheral.name;
cell.detailTextLabel.text = peripheral.identifier.UUIDString;
return cell;
}
#pragma mark - Private methods
- (void)discover {
self.title = @"Discovering ...";
[self.discoverer discoverDevicesNamed:nil withServices:nil andTimeout:DISCOVERY_TIMEOUT];
}
- (CBPeripheral *)peripheralForIndexPath:(NSIndexPath *)indexPath {
WLXDeviceDiscoveryData * discoveryData = self.discoverer.discoveredDevices[indexPath.row];
return discoveryData.peripheral;
}
@end
Once you discover the devices, is time to establish a connection with one of them. To do
this you need a WLXConnectionManager
. The WLXConnectionManager
is a protocol that
exposes an API to manage the connection with a device. As with the WLXDiscoverer
, you
ask for an object that implements the WLXConnectionManager
to the WLXBluetoothDeviceManager
through the method connectionManagerForPeripheral:usingReconnectionStrategy:
.
To obtain a WLXConnectionManager
you need a CBPeripheral
and WLXReconnectionStrategy
.
You can get the CBPeripheral
from the WLXDiscoveryData
. As for the reconnection strategy
you can create an instance of one the classes provided by the library or implement your own.
Once you get a WLXConnectionManager
you can start a connection using one of the
following methods: connectWithTimeout:
or connectWithTimeout:usingBlock:
. Both of this
methods require a timeout (in milliseconds). After the timeout expires, if the connection
cannot be established, an error will be raised. If you use the method that requires a block,
that block will get called with an error or nil if the connection was successfully established.
If you want to be notified about the connection status, you can implement the
WLXConnectionManagerDelegate
that provides methods for every possible scenario:
- Connection established
- Fail to connect
- Connection lost
- Connection terminated
- Did reconnect
- Will attempt to reconnect
The library provides a protocol to encapsulate the reconnection
logic, WLXReconnectionStrategy
. It also provides two concrete implementations:
WLXLinearReconnectionStrategy
and WLXNullReconnectionStrategy
.
The WLXNullReconnectionStrategy
, as its name suggests, does nothing. If the
connection is lost a connection lost error will immediately be raised.
The WLXLinearReconnectionStrategy
performs a linear back-off between each
reconnection attempt. Every time a connection is lost this strategy will check
if there are any remaining connection attempts. In such case, after waiting for
a certain amount of time, it will try to reconnect. If there are no remaining
reconnection attempts a connection lost error will be raised. To create an
instance of WLXLinearReconnectionStrategy
you must provide the following
parameters:
- a wait time, that is the amount of time (in milliseconds) to wait between each reconnection attempt.
- the maximum amount of reconnection attempts.
- a connection timeout, you can use the same one you use for the first connection
- a dispatch queue, we recommend to provide the same one the device
manager is using. You can getting by doing
deviceManager.queue
Here is an example of how to create a linear reconnection strategy:
WLXBluetoothDeviceManager * deviceManager = ...;
id<WLXReconnectionStrategy> reconnectionStrategy =
[[WLXLinearReconnectionStrategy alloc] initWithWaitTime:2000
maxReconnectionAttempts:3
connectionTimeout:3000
queue:deviceManager.queue];
The WLXConnectionManager
also publishes connection related notifications using
NSNotificationCenter
. The following is a list of the notifications:
-
WLXBluetoothDeviceConnectionEstablished
: Published every time a connection is established. You can get the peripheral from the user info using the keyWLXBluetoothDevicePeripheral
. -
WLXBluetoothDeviceReconnectionEstablished
: Published every time a reconnection is established. You can get the peripheral from the user info using the keyWLXBluetoothDevicePeripheral
. -
WLXBluetoothDeviceFailToConnect
: Published when a connection with a peripheral could not be established. You can get the error from theNSNotification
user info using the keyWLXBluetoothDeviceError
. -
WLXBluetoothDeviceConnectionLost
: Published when a established connection with a peripheral is lost. You can get the error and the peripheral from the user info dictionary using the keysWLXBluetoothDeviceError
andWLXBluetoothDevicePeripheral
. -
WLXBluetoothDeviceConnectionTerminated
: Published every time a theWLXConnecitonManager
disconnect
method is called and the connection is successfully terminated. -
WLXBluetoothDeviceReconnecting
: Published every time a reconnection attempt is about to be executed. You can get the remaining reconnection attempts from the user info dictionary using the keyWLXBluetoothDeviceRemainingReconnectionAttemps
.
A device registry encapsulates the logic of maintaining a registry of the
connected devices, so the next time you want to connect with a device you
do not have to discover a device a again. To create a new WLXBluetoothDeviceRegistry
you need an object that conforms to the WLXBluetoothDeviceRepository
protocol.
The repository is the one that actually stores the required information to later
be able to retrieve a peripheral. This information is encapsulated in a
WLXBluetoothDeviceConnectionRecord
.
Once you get an instance of WLXBluetoothDeviceRegistry
you have to enable it, then every time a new connection is established the
registry will save the connection record using the provided repository. To
enable the registry you should set the enabled
property to YES
.
You can have more than one device registry but you probably need one. Keep in mind that if you have more than one registry they should use different repositories.
The library provides an repository that stores the last connection record
into the user defaults, WLXBluetoothDeviceUserDefaultsRepository
but
you can have your own repository, say core data, by implementing the
protocol WLXBluetoothDeviceRepository
.
The following is an example of how to create a device registry using the user defaults repository:
WLXBluetoothDeviceManager * deviceManager = [WLXBluetoothDeviceManager deviceManager];
id<WLXBluetoothDeviceRepository> repository = [[WLXBluetoothDeviceUserDefaultsRepository alloc] initWithUserDefaults:[NSUserDefaults standardUserDefaults]];
id<WLXBluetoothDeviceRegistry> registry = [deviceManager deviceRegistryWithRepository:repository];
registry.enabled = YES;
Here is an example of how to establish a connection with a previously stored device:
id<WLXReconnectionStrategy> strategy = ...;
CBPeripheral * peripheral = registry.lastConnectedPeripheral;
id<WLXConnectionManager> connectionManager = [deviceManager connectionManagerForPeripheral:peripheral usingReconnectionStrategy:strategy];
[connectionManager connectWithTimeout:3000];
As you probably already know Bluetooth 4.0 devices expose services. Those services are basically a group of characteristics and they are grouped together because they share some common logic. Most probably you will endup organizing your Bluetooth application code that handles this logic in services and those services would probably have a one-to-one relation with the Bluetooth services. This is what we got in mind when we were designing the services API.
Once a connection has been established with a device you would need to
discover all its services. This is when you need the WLXServicesManager
.
The services manager can be obtained from a WLXConnectionManager
through
the servicesManager
property. This property will return nil
if the
connection was not established. After you get a reference to the services
manager, it does not matter if the connections gets lost; the services
manager is bound to the CBPeripheral
object. Once the connection is
reestablished you can still use the same services manager (same for all
its child objects).
With the WLXServicesManager
instance you can discover all the device's
services by calling discoverServicesUsingBlock:
. The block you pass to
this method will get called with an NSError
in case there is a problem
discovering the services or nil
if the discovery process succeed.
For each discovered service an instance of WLXServiceManager
is created.
This class is the one that is actually responsable of the interaction with
the Bluetooth service and its characteristics. In order to get an instance
of WLXServiceManager
you need the service's UUID and call
WLXServicesManager
's managerForService:
method.
The following is an example of how to get a WLXServiceManager
instance
for a particular service:
CBUUID * serviceUUID = [CBUUID UUIDWithString:@"68753A44-4D6F-1226-9C60-0050E4C00066"];
WLXServicesManager * servicesManager = connectionManager.servicesManager;
[servicesManager discoverServicesUsingBlock:^(NSError * error) {
if (error) {
NSLog(@"There was an error discovering the services");
} else {
WLXServiceManager * serviceManager = [servicesManager managerForService:serviceUUID];
}
}];
A characteristic is the "atomic" value in the Bluetooth 4.0 world. When you want to read o write a value from a Bluetooth device, you are reading or writing a characteristic.
The WLXServiceManager
exposes an API to read or write a service's
characteristic. You can also subscribe for notification for those
characteristic which provide notifications.
Using WLXServiceManager
to interact with the characteristic allows you
to not having to worry about discovery characteristics before you can write
o read them. All you need is the characteristic UUID and library will discovery
the characteristic if necessary.
Lets say you want to write to characteristic 68753A44-4D6F-1226-9C60-0050E4C00067
and
you don't care to be acknowledge if the write was successful. All you need to
do is call the writeValue:forCharacteristicUUID:
method from WLXServiceManager
and
the library will automatically discover the characteristic and then perform a write.
Once the characteristic is discovered, it gets cached so other operations don't
have to discover it again.
If you want to read a value from a characteristic you should invoke
the readValueForCharacteristicUUID:usingBlock:
. Suppose you have
a characteristic with UUID 68753A44-4D6F-1226-9C60-0050E4C00067
that
holds an NSUInteger
value. The following is an example of how
would you read that value:
CBUUID * characteristicUUID = [CBUUID UUIDWithString:@"68753A44-4D6F-1226-9C60-0050E4C00067"];
[serviceManager readValueForCharacteristicUUID:characteristicUUID usingBlock:^(NSError * error, NSData * data) {
if (error) {
NSLog(@"There was an error reading characteristic %@: %@", characteristicUUID.UUIDString, error);
} else {
NSUInteger value = *((NSUInteger *)data.bytes);
NSLog(@"Read value is %ul", (unsigned long)value);
}
}];
There are two ways of writing a characteristic's value. You can write
passing a callback block or without a callback block, writeValue:forCharacteristicUUID:usingBlock:
or writeValue:forCharacteristicUUID:
. If you use the version with the callback
block, it will translate to a write of type CBCharacteristicWriteWithResponse
. If
you don't provide a callback the write will be of
type CBCharacteristicWriteWithoutResponse
, in this case there is no way
of knowing if the write succeed or not.
CBUUID * characteristicUUID = [CBUUID UUIDWithString:@"68753A44-4D6F-1226-9C60-0050E4C00067"];
NSUInteger value = 10;
NSData * data = [NSData dataWithBytes:&value length:sizeof(value)];
[serviceManager writeValue:data forCharacteristicUUID:characteristicUUID usingBlock:^(NSError * error) {
if (error) {
NSLog(@"There was an error writing characteristic %@: %@", characteristicUUID.UUIDString, error);
}
}];
There are some characteristics that allow notifications, meaning every
time the characteristic value gets updated you will get a notification.
To enable and disable notifications for a characteristic you can use
the following methods enableNotificationsForCharacteristic:usingBlock:
and
disableNotificationsForCharacteristic:usingBlock:
.
After you enable notifications for a characteristic you have to subscribe
an observer for that characteristic using one of the following methods:
addObserverForCharacteristic:usingBlock:
or addObserverForCharacteristic:selector:target:
.
You can then remove observers using the removeObserver:
method.
CBUUID * characteristicUUID = [CBUUID UUIDWithString:@"68753A44-4D6F-1226-9C60-0050E4C00067"];
[serviceManager enableNotificationsForCharacteristic:characteristicUUID usingBlock:^(NSError * error) {
if (error) {
NSLog(@"There was an error enabling notifications for characteristic %@", characteristicUUID.UUIDString);
} else {
serviceManager addObserverForCharacteristic:characteristicUUID usingBlock:^(NSError * error, NSData * data) {
if (error) {
NSLog(@"There was an error reading characteristic %@: %@", characteristicUUID.UUIDString, error);
} else {
NSUInteger value = *((NSUInteger *)data.bytes);
NSLog(@"Value updated to %lu", (unsigned long)value);
}
}];
}
}];
The following is an example of how we suggest Bluetooth based services should be implemented using this library. Lets say that we a thermostat device that provides two services: a temperature service and a battery service.
The temperature service exposes a read/write characteristic that allows you to get/set the thermostat temperature and a another characteristic that allows you to set the temperature units: Fahrenheit or Celsius
The battery services exposes a read/notify characteristic that allows you to get the battery level.
The following is an example of how you could implement such services using
WLXServiceManager
:
TemperatureService.h
typedef enum TemperatureUnit {
TemperatureUnitFahrenheit,
TemperatureUnitCelsius
} TemperatureUnit;
@interface TemperatureService : NSObject
@property (nonatomic, readonly) WLXServiceManager * serviceManager;
+ (CBUUID *)serviceUUID;
- (instancetype)initWithServiceManager:(WLXServiceManager *)serviceManager;
- (void)readTemperatureUsingBlock:(void(^)(NSError *, CGFloat));
- (void)setTemperature:(CGFloat)temperature usingBlock:(void(^)(NSError *))block;
- (void)setTemperatureUnit:(TemperatureUnit)unit usingBlock:(void(^)(NSError *))block;
@end
TemperatureService.m
static NSString * serviceUUIDString = @"68753A44-4D6F-1226-9C60-0050E4C00066";
static NSString * temperatureCharacteristicUUIDString = @"68753A44-4D6F-1226-9C60-0050E4C00067";
static NSString * temperatureUnitCharacteristicUUID = @"68753A44-4D6F-1226-9C60-0050E4C00068";
static CBUUID * serviceUUID;
static CBUUID * temperatureCharacteristicUUID;
static CBUUID * temperatureUnitCharacteristicUUID;
@implementation TemperatureService
+ (void)initialize {
serviceUUID = [CBUUID UUIDWithString:serviceUUIDString];
temperatureCharacteristicUUID = [CBUUID UUIDWithString:temperatureCharacteristicUUIDString];
temperatureUnitCharacteristicUUID = [CBUUID UUIDWithString:temperatureUnitCharacteristicUUIDString];
}
+ (CBUUID *)serviceUUID {
return serviceUUID;
}
- (instancetype)initWithServiceManager:(WLXServiceManager *)serviceManager {
self = [super init];
if (self) {
_serviceManager = serviceManager;
}
return self;
}
- (void)readTemperatureUsingBlock:(void(^)(NSError *, CGFloat))block {
[self.serviceManager readValueForCharacteristicUUID:temperatureCharacteriscticUUID usingBlock:^(NSError * error, NSData * data) {
if (error) {
block(error, 0.0);
} else {
CGFloat value = *((CGFloat *)data.bytes);
block(nil, value);
}
}];
}
- (void)setTemperature:(CGFloat)temperature usingBlock:(void(^)(NSError *))block {
NSData * data = [NSData dataWithBytes:&temperature size:sizeof(temperature)];
[self.serviceManager writeValue:data forCharacteristicUUID:temperatureCharacteristicUUID usingBlock:block];
}
- (void)setTemperatureUnit:(TemperatureUnit)unit usingBlock:(void(^)(NSError *))block {
NSData * data = [NSData dataWithBytes:&unit size:sizeof(unit)];
[self.serviceManager writeValue:data forCharacteristicUUID:temperatureUnitCharacteristicUUID usingBlock:block];
}
@end
BatteryService.h
@interface BatteryService : NSObject
@property (nonatomic, readonly) WLXServiceManager * serviceManager;
@property (nonatomic) BOOL notificationsEnabled;
@property (nonoatomic, copy) void (^notificationsErrorHandler)(NSError *);
+ (CBUUID *)serviceUUID;
- (instancetype)initWithServiceManager:(WLXServiceManager *)serviceManager;
- (void)readBatteryLevelUsingBlock:(void(^)(NSError *, NSUInteger))block;
- (id)addBatteryLevelObserver:(void(^)(NSError *, NSUInteger))observer;
- (void)removeBatteryLevelObserver:(id)observer;
@end
BatteryService.m
static NSString * batteryCharacteristicUUIDString = @"68753A44-4D6F-1226-9C60-0050E4C00069";
static NSString * serviceUUIDString = @"68753A44-4D6F-1226-9C60-0050E4C00070";
static CBUUID * serviceUUID;
static CBUUID * batteryCharacteristicUUID;
@implementation BatteryService
+ (void)initialize {
serviceUUID = [CBUUID UUIDWithString:serviceUUIDString];
batteryCharacteristicUUID = [CBUUID UUIDWithString:batteryCharacteristicUUIDString];
}
+ (CBUUID *)serviceUUID {
return serviceUUID;
}
- (instancetype)initWithServiceManager:(WLXServiceManager *)serviceManager {
self = [super init];
if (self) {
_serviceManager = serviceManager;
_notificationsEnabled = NO;
}
return self;
}
- (void)setNotificationsEnabled:(BOOL)enabled {
if (enabled == _notificationsEnabled) {
return;
}
_notificationsEnabled = enabled;
if (enabled) {
[self.serviceManager enableNotificationsForCharacteristic:batteryCharacteristicUUID usingBlock:^(NSError * error) {
if (error) {
_notificationsEnabled = NO;
if (self.notificationsErrorHandler) {
notificationsErrorHandler(error);
}
}
}];
} else {
[self.serviceManager disableNotificationsForCharacteristic:batteryCharacteristicUUID usingBlock:^(NSError * error) {
if (error) {
_notificationsEnabled = YES;
if (self.notificationsErrorHandler) {
notificationsErrorHandler(error);
}
}
}];
}
}
- (void)readBatteryLevelUsingBlock:(void(^)(NSError *, NSUInteger))block {
[self.serviceManager readValueForCharacteristicUUID:batteryCharacteristicUUID usingBlock:^(NSError * error, NSData * data) {
if (error) {
block(error, 0);
} else {
NSUInteger value = *((NSUInteger *)data.bytes);
block(nil, value);
}
}];
}
- (id)addBatteryLevelObserver:(void(^)(NSError *, NSUInteger))observer {
return [self.serviceManager addObserverForCharacteristic:batteryCharacteristicUUID usingBlock:^(NSError * error, NSData * data) {
if (error) {
block(error, 0);
} else {
NSUInteger value = *((NSUInteger *)data.bytes);
block(nil, value);
}
}];
}
- (void)removeBatteryLevelObserver:(id)observer {
[self.serviceManager removeObserver:observer];
}
@end
Apart from the dispatch queue that the WLXBluetoothDeviceManager
uses
for all the Bluetooth operations, each WLXServiceManager
creates its own
serial dispatch queue. Once a bluetooth event gets dispatched to the
service manager all the service related operations, like executing observers
or callback blocks, are executed in a dedicated serial dispatch queue.
This is done to prevent "slow clients" to congest other services. For example if one service is notifying a lot of notifications and the registered observers are taking too long to process those notifications, the only service that will be affected will be the once with "slow clients".
There also are notifications triggered by the library every time the bluetooth service is powered on/off:
WLXBluetoothDeviceBluetoothIsOn
WLXBluetoothDeviceBluetoothIsOff
WLXBluetoothDeviceBluetoothPowerStatusChanged
TODO