I'm trying to launch BLE in both Central and Peripheral modes. With hardcoded variables for now for sake of simplicity.
I think i've implemented everything according to the docs.
I can check if Peripheral mode is working using Android smartphone (api-19, do not support Peripheral mode). iPhone shows up correctly when i'm using MyBeacon app, for example.
However it doesn't show up when i'm running this code in my app:
Here is .h
:
#import <RCTBridgeModule.h>
#import <RCTEventEmitter.h>
@import CoreBluetooth;
@import QuartzCore;
@interface BTManager : RCTEventEmitter <RCTBridgeModule, CBCentralManagerDelegate, CBPeripheralManagerDelegate, CBPeripheralDelegate>
@property (nonatomic, strong) CBCentralManager *centralManager;
@property (nonatomic, strong) CBPeripheralManager *peripheralManager;
@property (nonatomic, strong) CBMutableCharacteristic *transferCharacteristic;
@end
And .m
:
#import "BTManager.h"
@implementation BTManager
RCT_EXPORT_MODULE();
- (NSArray<NSString *> *)supportedEvents
{
return @[@"BTManagerDeviceFound", @"BTManagerStatus"];
}
- (void)viewDidLoad
{
CBCentralManager *centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
self.centralManager = centralManager;
CBPeripheralManager *peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];
self.peripheralManager = peripheralManager;
}
RCT_EXPORT_METHOD(start:(NSDictionary *)options)
{
[self.centralManager scanForPeripheralsWithServices:nil options:nil];
self.transferCharacteristic = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:@"EB6727C4-F184-497A-A656-76B0CDAC633A"] properties:CBCharacteristicPropertyRead value:nil permissions:CBAttributePermissionsReadable];
CBMutableService *transferService = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:@"EB6727C4-F184-497A-A656-76B0CDAC633A"] primary:YES];
transferService.characteristics = @[self.transferCharacteristic];
[self.peripheralManager addService:transferService];
[self.peripheralManager startAdvertising:@{ CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:@"FB694B90-F49E-4597-8306-171BBA78F846"]] }];
NSLog(@"Started");
}
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {
// log peripheralManager state
NSLog(@"peripheralManagerDidUpdateState peripheral %@", peripheral);
}
- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error
{
// log centralManager state
NSLog(@"peripheralManager didAddService peripheral %@", peripheral);
NSLog(@"peripheralManager didAddService service %@", service);
NSLog(@"peripheralManager didAddService error %@", error);
}
- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error
{
NSLog(@"peripheralManagerDidStartAdvertising peripheral %@", peripheral);
NSLog(@"peripheralManagerDidStartAdvertising error %@", error);
}
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
[self sendEventWithName:@"BTManagerDeviceFound" body:advertisementData];
}
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
NSLog(@"centralManagerDidUpdateState central %@", central);
}
RCT_EXPORT_METHOD(stop:(NSDictionary *)options)
{
// remove all related processes, send event to js
[self sendEventWithName:@"BTManagerStatus" body:@"details here"];
// [self.myCentralManager stopScan];
}
@end
None of the event listeners above are firing, except NSLog(@"Started");
I've got this suggestion:
On a related subject, make sure that whatever code creates and executes your BTManager allows the object to live for long enough for the bluetooth actions to be performed. If it goes out of scope or gets garbage collected, you'll have the same problem.
I do not know how to check if this is true or not.
Also, this tutorial is using this method:
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {
if (peripheral.state != CBPeripheralManagerStatePoweredOn) {
return;
}
if (peripheral.state == CBPeripheralManagerStatePoweredOn) {
self.transferCharacteristic = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_UUID] properties:CBCharacteristicPropertyNotify value:nil permissions:CBAttributePermissionsReadable];
CBMutableService *transferService = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID] primary:YES];
transferService.characteristics = @[_transferCharacteristic];
[_peripheralManager addService:transferService];
}
}
... but the docs contain nothing related to this CBPeripheralManagerStatePoweredOn
or so. The article is 4 years old, so it may be not even relevant now.
Oh, yeah, and also i'm barely familiar with objective-c, so the mistake there may be very simple.
Thank you for reading till here :)
Apparently, viewDidLoad
never being initiated. Therefore I've moved code from it to start
method, and code from start
into ...DidUpdateState
s methods, as @LarsBlumberg suggested.
#import "BTManager.h"
#import <React/RCTLog.h>
@implementation BTManager
RCT_EXPORT_MODULE();
- (NSArray<NSString *> *)supportedEvents
{
return @[@"BTManagerDeviceFound", @"BTManagerStatus"];
}
RCT_EXPORT_METHOD(start:(NSDictionary *)options)
{
RCTLogInfo(@"Start?");
CBCentralManager *centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:@{CBCentralManagerOptionShowPowerAlertKey: @(YES)}];
self.centralManager = centralManager;
CBPeripheralManager *peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];
self.peripheralManager = peripheralManager;
RCTLogInfo(@"Started");
}
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {
// log peripheralManager state
RCTLogInfo(@"peripheralManagerDidUpdateState peripheral %ld", (long)peripheral.state);
if (peripheral.state != CBPeripheralManagerStatePoweredOn) {
RCTLogInfo(@"Peripheral is not powered on");
return;
}
self.transferCharacteristic = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:@"EB6727C4-F184-497A-A656-76B0CDAC633A"] properties:CBCharacteristicPropertyRead value:nil permissions:CBAttributePermissionsReadable];
CBMutableService *transferService = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:@"EB6727C4-F184-497A-A656-76B0CDAC633A"] primary:YES];
transferService.characteristics = @[self.transferCharacteristic];
[peripheral addService:transferService];
NSDictionary *advertisingData = @{CBAdvertisementDataLocalNameKey : @"yphone", CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:@"EBA38950-0D9B-4DBA-B0DF-BC7196DD44FC"]]};
[peripheral startAdvertising:advertisingData];
}
- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error
{
// log centralManager state
RCTLogInfo(@"peripheralManager didAddService peripheral %@", peripheral);
RCTLogInfo(@"peripheralManager didAddService service %@", service);
RCTLogInfo(@"peripheralManager didAddService error %@", error);
}
- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error
{
RCTLogInfo(@"peripheralManagerDidStartAdvertising peripheral %@", peripheral);
RCTLogInfo(@"peripheralManagerDidStartAdvertising error %@", error);
}
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
[self sendEventWithName:@"BTManagerDeviceFound" body:advertisementData];
}
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
RCTLogInfo(@"centralManagerDidUpdateState central %ld", (long)central.state);
// if (central.state == CBCentralManagerStatePoweredOff) {
// UIAlertView *alert = [[UIAlertView alloc] initWithTitle: @"Error" message: @"Please turn on Bluetooth in Settings" delegate: nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
// [alert show];
// }
if (central.state != CBCentralManagerStatePoweredOn) {
RCTLogInfo(@"Central is not powered on");
return;
}
[central scanForPeripheralsWithServices:nil options:nil];
}
RCT_EXPORT_METHOD(stop:(NSDictionary *)options)
{
// remove all related processes, send event to js
[self sendEventWithName:@"BTManagerStatus" body:@"details here"];
// [self.myCentralManager stopScan];
}
@end
Now everything goes ok till state updates. Both central.state
and peripheral.state
returns 4
, while CB...ManagerStatePoweredOn
is equal to 5
. How do i make it equal to 5
? App is requesting access to bt, but iphone is not enabling it. When it's enabled manually, everything seems work well.
You are probably adding the service transferService
too early to the peripheral manager and you are also probably starting the advertisement too early.
You have to wait until peripheralManagerDidUpdateState
reports that your peripheral manager's state has reached CBPeripheralManagerStatePoweredOn
.
This said, you can either move the code from RCT_EXPORT_METHOD(start:(NSDictionary *)options)
into your empty peripheralManagerDidUpdateState
implementation.
Alternatively, if you only want to add services to your peripheralManager
and then start advertising when somebody has called your start
method (looks like you're creating a React Native binding?), make sure that self.peripheralManager.state
is CBPeripheralManagerStatePoweredOn
within your start
method. This however requires the caller of start
(your React Native JS code) to not call this method too early.
RCT_EXPORT_METHOD(start:(NSDictionary *)options)
{
NSLog(@"Start?");
if (self.peripheralManager.state != CBPeripheralManagerStatePoweredOn) {
NSLog(@"Peripheral is not powered on");
return;
}
if (self.centralManager.state != CBCentralManagerStatePoweredOn) {
NSLog(@"Central is not powered on");
return;
}
NSLog(@"OK, peripheral and central are both powered on");
[self.centralManager scanForPeripheralsWithServices:nil options:nil];
self.transferCharacteristic = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:@"EB6727C4-F184-497A-A656-76B0CDAC633A"] properties:CBCharacteristicPropertyRead value:nil permissions:CBAttributePermissionsReadable];
CBMutableService *transferService = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:@"EB6727C4-F184-497A-A656-76B0CDAC633A"] primary:YES];
transferService.characteristics = @[self.transferCharacteristic];
[self.peripheralManager addService:transferService];
[self.peripheralManager startAdvertising:@{ CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:@"FB694B90-F49E-4597-8306-171BBA78F846"]] }];
NSLog(@"Started");
}
Similarly you can only start scanning for peripherals with your centralManager
if it has the "powered on" state.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With