Reputation: 13537
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'm unable to check if Central mode works, but when the code contained only Central-mode-related code, the app was asking to enable BT, and wasn't crashing so i assume it was scanning properly :) I'll check this later.
After i've added Peripheral mode in there, app started freezing once i've initiating start
method.
Xcode shows very informative log:
libsystem_kernel.dylib`__abort_with_payload:
0x18acc5d6c <+0>: movz x16, #0x209
0x18acc5d70 <+4>: svc #0x80
-> 0x18acc5d74 <+8>: b.lo 0x18acc5d8c ; <+32>
0x18acc5d78 <+12>: stp x29, x30, [sp, #-16]!
0x18acc5d7c <+16>: mov x29, sp
0x18acc5d80 <+20>: bl 0x18acaa7d0 ; cerror_nocancel
0x18acc5d84 <+24>: mov sp, x29
0x18acc5d88 <+28>: ldp x29, x30, [sp], #16
0x18acc5d8c <+32>: ret
Nothing else, no errors, no warnings etc. This is not related to Central mode, because if i'm removing it, nothing changes.
So, what does it mean? I've googled a bit and found misc assumptions about permission descriptions, iOS 7 etc.
I'm barely familiar with objective-c, so sorry, if i'm asking very simple question :)
sendEventWithName
in the code below works like logs atm. The only log i'm receiving is started
(at the end of start
method`)
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"]] }];
[self sendEventWithName:@"BTManagerStatus" body:@"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
Upvotes: 0
Views: 349
Reputation: 64941
A few thoughts:
The code declares CBCentralManager *centralManager
and CBPeripheralManager *peripheralManager
as local variables inside the start method. These variables will go out of scope when the method ends and cease to exist, preventing bluetooth operations from continuing. You need to declare these as class-level variables, then initialize them in the the start method. 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.
Code should not perform actions on either the central or the peripheral until after receiving a powered on callback to: peripheralManagerDidUpdateState
or centralManagerDidUpdateState
, respectively. I would move the code that scans or sets up advertising to new initialization methods for peripheral/central, and call them when you get a callback to those methods with a value of CBPeripheralManagerStatePoweredOn
or CBCentalManagerStatePoweredOn
, respectively. Even if the interface is powered on when you code starts, CoreBluetooth won't let you do anything successfully until it has fully initialized and sent the powered on callback, so you must always wait for this.
Make sure you trust your custom logging implementation. When in doubt, NSLog(@"XXXX") is a handy tool for a sanity check.
Upvotes: 1