Search code examples
iosobjective-cbluetoothcore-bluetoothcbperipheralmanager

iOS Bluetooth peripheralManagerDidUpdateState never called


I'm trying to set up my iPhone to act as a heart rate monitor and send information using the standard heart rate service so that the app I have running on my PC can retrieve the data. I'm a newbie to iOS but I have got bluetooth stuff running on Android and Windows before. I'm following the information here and I'm falling at the first hurdle...

As specified in the doc, I first call

CBPeripheralManager *myPeripheralManager = [[CBPeripheralManager alloc] initWithDelegate:(id<CBPeripheralManagerDelegate>)self queue:nil options:nil];

I also implement the peripheralManagerDidUpdateState callback but here lies the problem. This callback never runs! I have waited for minutes and nothing has happened. I suspect I'm missing a very simple step because I have searched all day and have found nothing online! Can anyone explain the explicit steps to take here both in code and in physically doing stuff with my iPhone? Does it have to have a connection before this callback runs? Does it have to be paired to anything?

Here's the full code. It all runs fine, I'm getting no errors at all.

#import "ViewController.h"
@import CoreBluetooth;

@interface ViewController()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    NSLog(@"App running...");

    //Create the peripheral manager
    CBPeripheralManager *myPeripheralManager = [[CBPeripheralManager alloc] initWithDelegate:(id<CBPeripheralManagerDelegate>)self queue:nil options:nil];
}

- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral{
    int state = peripheral.state;
    NSLog(@"Peripheral manager state =  %d", state);

    //Set the UUIDs for service and characteristic
    CBUUID *heartRateServiceUUID = [CBUUID UUIDWithString: @"180D"];
    CBUUID *heartRateCharacteristicUUID = [CBUUID UUIDWithString:@"2A37"];
    CBUUID *heartRateSensorLocationCharacteristicUUID = [CBUUID UUIDWithString:@"0x2A38"];


    //char heartRateData[2]; heartRateData[0] = 0; heartRateData[1] = 60;

    //Create the characteristics
    CBMutableCharacteristic *heartRateCharacteristic =
    [[CBMutableCharacteristic alloc] initWithType:heartRateCharacteristicUUID
                                       properties: CBCharacteristicPropertyNotify
                                            value:nil
                                      permissions:CBAttributePermissionsReadable];

    CBMutableCharacteristic *heartRateSensorLocationCharacteristic =
    [[CBMutableCharacteristic alloc] initWithType:heartRateSensorLocationCharacteristicUUID
                                       properties:CBCharacteristicPropertyRead
                                            value:nil
                                      permissions:CBAttributePermissionsReadable];
    //Create the service
    CBMutableService *myService = [[CBMutableService alloc] initWithType:heartRateServiceUUID primary:YES];
    myService.characteristics = @[heartRateCharacteristic, heartRateSensorLocationCharacteristic];

    //Publish the service
    NSLog(@"Attempting to publish service...");
    [peripheral addService:myService];

    //Set the data
    NSDictionary *data = @{CBAdvertisementDataLocalNameKey:@"iDeviceName",
                           CBAdvertisementDataServiceUUIDsKey:@[[CBUUID UUIDWithString:@"180D"]]};

    //Advertise the service
    NSLog(@"Attempting to advertise service...");
    [peripheral startAdvertising:data];

}

- (void)peripheralManager:(CBPeripheralManager *)peripheral
            didAddService:(CBService *)service
                    error:(NSError *)error {

    if (error) {
        NSLog(@"Error publishing service: %@", [error localizedDescription]);
    }
    else NSLog(@"Service successfully published");
}

- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral
                                       error:(NSError *)error {

    if (error) {
        NSLog(@"Error advertising: %@", [error localizedDescription]);
    }
    else NSLog(@"Service successfully advertising");
}

@end

Solution

  • The object myPeripheralManager is deallocated as soon as viewDidLoad method returns, as you have only one reference pointing to this object and it goes out of scope.

    The solution is to create a property in ViewController which references the instance of CBPeripheralManager:

    @interface ViewController()
    @property (nonatomic, strong) CBPeripheralManager *myPeripheralManager;
    @end
    

    and then initialise the property in viewDidLoad:

    self.myPeripheralManager = myPeripheralManager = [[CBPeripheralManager alloc] initWithDelegate:(id<CBPeripheralManagerDelegate>)self queue:nil options:nil];
    

    You may want to read more about memory management (in particular, Automatic Reference Counting) in Objective-C.