iPhone 6 running iOS 9 (as peripheral) terminates encryption request with 0x3D

I reported this problem last month in the iOS 9 beta forum ( https://forums.developer.apple.com/thread/17456 ), but since that's closing down, I'll report more details here.


We are developing an iOS app where the iOS device is the peripheral. The central uses a Nordic BLE chip.


Everything was working swimmingly up until iOS 9 betas. In iOS 8 and below (even on iPhone 6 and 6 Plus), it works correctly:

  1. iOS app advertises
  2. Nordic chip connects
  3. Nordic chip sends encryption request
  4. Both devices exchange LL_Version_Ind with VersionNr 0x07
  5. iOS sends Encryption_RSP and Start_Encryption_Req


However, on iOS 9 (and seemingly only with devices Apple now categorizes as BLE 4.2-compliant), iOS ends up sending a termination:

  1. iOS app advertises
  2. Nordic chip connects
  3. Nordic chip sends encryption request
  4. Both devices exchange LL_Version_Ind; Nordic chip sends VersionNr 0x07 but iOS sends VersionNr 0x08.
  5. iOS then sends Terminate_Ind with LL_Terminate_Ind errorCode of 0x3D.


It seems to be a combination of iPhone 6 (any model: 6, 6s, 6+, 6s+) and iOS 9 that causes the problem (BLE 4.2 stack?). An iPhone 6 running iOS 8.1 works correctly (BLE 4.0 stack), and an iPad Air 1 with iOS 9.0.2 (BLE 4.0 stack) works correctly.


But any iPhone 6 model (6, 6+, 6s) with iOS 9 terminates the connection.


Note that, even on devices that return the error code, the device appears to have bonded, as there is a row in the Bluetooth Settings for the device.

Replies

This does not appear to be fixed by iOS 9.1.

We're also using the nordic BLE controller. But we Use the iPhone as Central.


It's seems to be connected with the error you have detected.


Our setup for this error is:



- the device BLE controller is a nordic N51822

- tested with iPhone A1429 (but fails also on all other iPhones with BLE4.x capabilities)

- iOS Build is 9.1 (13B143) (but also fails with all iOS version from 9.x up, even all the betas)



So what I've done is:



In code i use some constants for convenience:



#define findMeServiceUUID             [CBUUID UUIDWithString:@"1802"]
#define genericAccessServiceUUID      [CBUUID UUIDWithString:@"1800"]
#define linkLossServiceUUID           [CBUUID UUIDWithString:@"1803"]
#define deviceNameCharacteristicUUID  [CBUUID UUIDWithString:@"2A00"]
#define alertLevelCharacteristicUUID  [CBUUID UUIDWithString:@"2A06"]
#define BT_ALERT_LEVEL_NONE 0
#define BT_LEVEL_MILD 1
#define BT_ALERT_LEVEL_HIGH 2
#define BT_ALERT_LEVEL_RESERVED(LVL) LVL


1. Scan for peripherals with findMe and linkLoss Service (1802 and 1803):



  NSDictionary* scanOptions = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:NO]
                                                          forKey:CBCentralManagerScanOptionAllowDuplicatesKey];
  NSArray* serviceArray = [NSArray arrayWithObjects:findMeServiceUUID,linkLossServiceUUID, nil];


  [centralManager scanForPeripheralsWithServices:serviceArray options:scanOptions];


2. When found an peripheral i add the CBPeripheral* to an mutable array used as datasource for a table view displaying the scan results:



- (void) centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
  NSLog(@"INFO: Did discover peripheral <%@> with identifier <%@>", peripheral.name, peripheral.identifier);
  [peripherals addObject:peripheral];
  // Update UITableView
  [self reloadTable];
}


3. Selected the peripheral in table view (set as CBPeripheral* selectedPeripheral) and connected to it with:



  NSDictionary* connectOptions = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES]
                                                             forKey:CBConnectPeripheralOptionNotifyOnDisconnectionKey];
  [centralManager connectPeripheral:selectedPeripheral options:connectOptions];


4. When connected I start the service discovery



- (void) centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
  NSLog(@"INFO: Did connect to peripheral <%@> with identifier <%@>", peripheral.name, peripheral.identifier);


  isConnected = YES;


  // Update the info UILabel
  [self updateLabel];


  // Start service discovery
  [peripheral discoverServices:nil];
}


5. When a service is discoverd i start to discover the service characteristics:



- (void) peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
  NSLog(@"INFO: Did discover %d services on %@", (int)[peripheral services].count, [peripheral name]);
  for(CBService *s in [peripheral services]) {
    if([[s UUID] isEqual:findMeServiceUUID])
      immediateAlertService = s;


    if([[s UUID] isEqual:linkLossServiceUUID])
      linkLossService = s;


    NSLog(@"INFO: Did discover service %@ on %@", [s.UUID representativeString], [peripheral name]);
    [peripheral discoverCharacteristics:[NSArray arrayWithObject:alertLevelCharacteristicUUID] forService:s];
  }
}


6. When discoverd a characterisics for a service i store thr references to a variable.



- (void) peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
  NSLog(@"INFO: Did discover %d characteristics for service %@ on peripheral %@", (int)service.characteristics.count, service, peripheral);
  for(CBCharacteristic *c in [service characteristics])
  {
    NSLog(@"INFO: Did discover characteristic %@ for service %@ on peripheral %@", [c.UUID representativeString], service, peripheral);
    if([service isEqual:immediateAlertService] &&
       [[c UUID] isEqual:alertLevelCharacteristicUUID])
      immediateAlertAlertLevelCharacteristic = c;


    else if([service isEqual:linkLossService] &&
            [[c UUID] isEqual:alertLevelCharacteristicUUID]) {


      if(isBonded)
        return;


      linkLossAlertLevelCharacteristic = c;
      if(linkLossAlertLevelCharacteristic)
        [self writeLinkLossAlert:BT_ALERT_LEVEL_NONE];


    }
  }
}


7. As you can see if linkLossAlertLevelCharacteristic is discovered i immediately write BT_ALERT_LEVEL_NONE ( = 0 ) to it, to initialize bonding:



- (void) writeLinkLossAlertLevelToTag:(UInt8) linkLossAlertLevel
{
  NSData* data = [NSData dataWithBytes:&linkLossAlertLevel length:1];
  [selectedPeripheral writeValue:data forCharacteristic:linkLossAlertLevelCharacteristic type:CBCharacteristicWriteWithResponse];
}
- (bool) writeLinkLossAlert:(UInt8)level
{
  if(linkLossAlertLevelCharacteristic != nil) {
    NSLog(@"INFO: Writes link loss alert %d to peripheral...", level);
    [self writeLinkLossAlertLevelToTag:level];
    return YES;
  }
  NSLog(@"ERROR: Couldn´t write to alert level on link loss service.");
  return NO;
}


8. The "Pairing" dialog appears and is confirmed to pair with. But instead of pairing the write fails and there after i also get a disconnect with the timeout error.



It works all fine until iOS 9 and i don't use any deprecated API's



I logged the delegate callbacks from CBCentralManager and CBPeripheral:



2015-10-30 11:38:07.881 BLEControl[307:32547] INFO: Bluetooth is enabled

2015-10-30 11:38:14.423 BLEControl[307:32547] INFO: Start to scan for peripherals

2015-10-30 11:38:14.456 BLEControl[307:32547] INFO: Did discover peripheral <Torantrieb> with identifier <<__NSConcreteUUID 0x15d70340> 0545B850-B6BE-1BB9-10E7-80E3559AC9FB>

2015-10-30 11:38:18.129 BLEControl[307:32547] INFO: Stopped to scan for peripherals

2015-10-30 11:38:18.164 BLEControl[307:32547] INFO: Did connect to peripheral <Torantrieb> with identifier <<__NSConcreteUUID 0x15d70340> 0545B850-B6BE-1BB9-10E7-80E3559AC9FB>

2015-10-30 11:38:18.362 BLEControl[307:32547] INFO: Did discover 3 services on Torantrieb

2015-10-30 11:38:18.363 BLEControl[307:32547] INFO: Did discover service 1804 on Torantrieb

2015-10-30 11:38:18.364 BLEControl[307:32547] INFO: Did discover service 1802 on Torantrieb

2015-10-30 11:38:18.364 BLEControl[307:32547] INFO: Did discover service 1803 on Torantrieb

2015-10-30 11:38:18.366 BLEControl[307:32547] INFO: Did discover 0 characteristics for service <CBService: 0x15d85a00, isPrimary = YES, UUID = 1804> on peripheral <CBPeripheral: 0x15d85de0, identifier = 0545B850-B6BE-1BB9-10E7-80E3559AC9FB, name = Torantrieb, state = connected>

2015-10-30 11:38:18.367 BLEControl[307:32547] INFO: Did discover 1 characteristics for service <CBService: 0x15d8d5e0, isPrimary = YES, UUID = 1802> on peripheral <CBPeripheral: 0x15d85de0, identifier = 0545B850-B6BE-1BB9-10E7-80E3559AC9FB, name = Torantrieb, state = connected>

2015-10-30 11:38:18.367 BLEControl[307:32547] INFO: Did discover characteristic 2a06 for service <CBService: 0x15d8d5e0, isPrimary = YES, UUID = 1802> on peripheral <CBPeripheral: 0x15d85de0, identifier = 0545B850-B6BE-1BB9-10E7-80E3559AC9FB, name = Torantrieb, state = connected>

2015-10-30 11:38:18.367 BLEControl[307:32547] INFO: Did discover 1 characteristics for service <CBService: 0x15d8d620, isPrimary = YES, UUID = 1803> on peripheral <CBPeripheral: 0x15d85de0, identifier = 0545B850-B6BE-1BB9-10E7-80E3559AC9FB, name = Torantrieb, state = connected>

2015-10-30 11:38:18.369 BLEControl[307:32547] INFO: Did discover characteristic 2a06 for service <CBService: 0x15d8d620, isPrimary = YES, UUID = 1803> on peripheral <CBPeripheral: 0x15d85de0, identifier = 0545B850-B6BE-1BB9-10E7-80E3559AC9FB, name = Torantrieb, state = connected>

2015-10-30 11:38:18.370 BLEControl[307:32547] INFO: Writes link loss alert 0 to peripheral...

2015-10-30 11:38:22.354 BLEControl[307:32547] ERROR: Failed to write value for characteristic <CBCharacteristic: 0x15d8d890, UUID = 2A06, properties = 0xA, value = (null), notifying = NO>, reason: Error Domain=CBATTErrorDomain Code=15 "Encryption is insufficient." UserInfo={NSLocalizedDescription=Encryption is insufficient.}

2015-10-30 11:38:23.145 BLEControl[307:32547] ERROR: Did disconnect peripheral <Torantrieb> with identifier <<__NSConcreteUUID 0x15d70340> 0545B850-B6BE-1BB9-10E7-80E3559AC9FB>: Error Domain=CBErrorDomain Code=6 "The connection has timed out unexpectedly." UserInfo={NSLocalizedDescription=The connection has timed out unexpectedly.} {

NSLocalizedDescription = "The connection has timed out unexpectedly.";

}



Here is the full code of my Test-View Controller



ViewController.h



#import <UIKit/UIKit.h>
#import <CoreBluetooth/CoreBluetooth.h>


@interface CBUUID (StringExtraction)
- (NSString *)representativeString;
@end


@interface ViewController : UIViewController <UITableViewDelegate,UITableViewDataSource,CBCentralManagerDelegate,CBPeripheralDelegate>
@end


ViewController.m



#import "ViewController.h"



#define findMeServiceUUID             [CBUUID UUIDWithString:@"1802"]
#define genericAccessServiceUUID      [CBUUID UUIDWithString:@"1800"]
#define linkLossServiceUUID           [CBUUID UUIDWithString:@"1803"]
#define deviceNameCharacteristicUUID  [CBUUID UUIDWithString:@"2A00"]
#define alertLevelCharacteristicUUID  [CBUUID UUIDWithString:@"2A06"]
#define BT_ALERT_LEVEL_NONE 0
#define BT_LEVEL_MILD 1
#define BT_ALERT_LEVEL_HIGH 2
#define BT_ALERT_LEVEL_RESERVED(LVL) LVL


@implementation CBUUID (StringExtraction)
- (NSString *)representativeString;
{
  NSData *data = [self data];

  NSUInteger bytesToConvert = [data length];
  const unsigned char *uuidBytes = [data bytes];
  NSMutableString *outputString = [NSMutableString stringWithCapacity:16];

  for (NSUInteger currentByteIndex = 0; currentByteIndex < bytesToConvert; currentByteIndex++)
  {
    switch (currentByteIndex)
    {
      case 3:
      case 5:
      case 7:
      case 9:[outputString appendFormat:@"%02x-", uuidBytes[currentByteIndex]]; break;
      default:[outputString appendFormat:@"%02x", uuidBytes[currentByteIndex]];
    }

  }
  return outputString;
}
@end


@interface ViewController ()
@end


@implementation ViewController {


  NSMutableArray* peripherals;
  BOOL isScanning;
  BOOL isPairing;
  BOOL isBonded;
  BOOL isConnected;
  BOOL hasFailed;

  CBCentralManager* centralManager;

  CBPeripheral* selectedPeripheral;


  CBService *immediateAlertService;
  CBCharacteristic *immediateAlertAlertLevelCharacteristic;

  CBService *linkLossService;
  CBCharacteristic *linkLossAlertLevelCharacteristic;

  IBOutlet UILabel* labelPeripheral;
  IBOutlet UIButton* buttonScan;
  IBOutlet UITableView* tablePeripherals;
}


-(id)initWithCoder:(NSCoder *)aDecoder {
  self = [super initWithCoder:aDecoder];
  if(!self)
    return self;

  peripherals = [[NSMutableArray alloc] init];
  centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
  return self;
}


- (void)viewDidLoad {
  [super viewDidLoad];
  [self updateLabel];
}


- (void)didReceiveMemoryWarning {
  [super didReceiveMemoryWarning];
}


#pragma mark - BLE


-(void)startScan {
  if(isScanning)
    return;

  [centralManager stopScan];
  [peripherals removeAllObjects];
  [self reloadTable];

  NSDictionary* scanOptions = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:NO]
                                                          forKey:CBCentralManagerScanOptionAllowDuplicatesKey];
  NSArray* serviceArray = [NSArray arrayWithObjects:findMeServiceUUID,linkLossServiceUUID, nil];

  [centralManager scanForPeripheralsWithServices:serviceArray options:scanOptions];

  NSLog(@"INFO: Start to scan for peripherals");
  isScanning = YES;
}
-(void)stopScan {

  /

  if(!isScanning)
    return;

  [centralManager stopScan];
  NSLog(@"INFO: Stopped to scan for peripherals");



  isScanning = NO;
}


-(void)pairPeripheral {

  if(!selectedPeripheral || isPairing)
    return;

  isPairing = YES;
  selectedPeripheral.delegate = self;

  [self updateLabel];

  [self connectPeripheral];
}


-(void)connectPeripheral {
  if(!selectedPeripheral || isConnected)
    return;

  NSDictionary* connectOptions = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES]
                                                             forKey:CBConnectPeripheralOptionNotifyOnDisconnectionKey];
  [centralManager connectPeripheral:selectedPeripheral options:connectOptions];
}


-(void)disconnectPeripheral {
  if(!selectedPeripheral || !isConnected)
    return;

  [centralManager cancelPeripheralConnection:selectedPeripheral];
}


-(void)alertPeripheral {
  if(!selectedPeripheral || !isBonded)
    return;

  [self writeImmediateAlert:BT_ALERT_LEVEL_RESERVED(3)];
}


- (void) writeLinkLossAlertLevelToTag:(UInt8) linkLossAlertLevel
{
  NSData* data = [NSData dataWithBytes:&linkLossAlertLevel length:1];
  [selectedPeripheral writeValue:data forCharacteristic:linkLossAlertLevelCharacteristic type:CBCharacteristicWriteWithResponse];
}


- (bool) writeLinkLossAlert:(UInt8)level
{
  if(linkLossAlertLevelCharacteristic != nil) {
    NSLog(@"INFO: Writes link loss alert %d to peripheral...", level);
    [self writeLinkLossAlertLevelToTag:level];
    return YES;
  }
  NSLog(@"ERROR: Couldn´t write to alert level on link loss service.");
  return NO;
}


- (void) writeImmediateAlertLevelToTag:(UInt8) immediateAlertLevel
{
  NSData* data = [NSData dataWithBytes:&immediateAlertLevel length:1];
  [selectedPeripheral writeValue:data forCharacteristic:immediateAlertAlertLevelCharacteristic type:CBCharacteristicWriteWithoutResponse];
}


- (bool) writeImmediateAlert:(UInt8)level
{
  if(immediateAlertAlertLevelCharacteristic != nil) {
    NSLog(@"INFO: Writes immediate alert %d to tag...", level);
    [self writeImmediateAlertLevelToTag:level];
    return YES;
  }
  NSLog(@"INFO: Couldn´t write to alert level on immediate alert service.");
  return NO;
}
#pragma mark - CentralManager delegate
- (void) centralManagerDidUpdateState:(CBCentralManager *)central
{
  if ([central state] == CBCentralManagerStatePoweredOn)
    NSLog(@"INFO: Bluetooth is enabled");
  else
  {
    NSLog(@"WARNING: Bluetooth is not enabled");
    [labelPeripheral setText:@"Bluetooth is not enabled!"];
  }
}
- (void) centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
  NSLog(@"INFO: Did discover peripheral <%@> with identifier <%@>", peripheral.name, peripheral.identifier);
  [peripherals addObject:peripheral];
  [self reloadTable];
}
- (void) centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
  NSLog(@"INFO: Did connect to peripheral <%@> with identifier <%@>", peripheral.name, peripheral.identifier);

  isConnected = YES;
  [self updateLabel];

  [peripheral discoverServices:nil];
}


- (void) centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
  isConnected = NO;
  if(error) {
    NSLog(@"ERROR: Did disconnect peripheral <%@>  with identifier <%@>: %@ %@", peripheral.name, peripheral.identifier, error, [error userInfo]);

    if(isPairing) {
      hasFailed = YES;
      isPairing = NO;
      [self updateLabel];
    }
    return;
  }
  [self updateLabel];
  NSLog(@"INFO: Did disconnect peripheral <%@> with identifier <%@>", peripheral.name, peripheral.identifier);
}


- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
  isPairing = NO;
  [self updateLabel];

  if(error) {
    NSLog(@"ERROR: Did failed to connect to peripheral <%@>  with identifier <%@>: %@ %@", peripheral.name, peripheral.identifier, error, [error userInfo]);
    return;
  }
  NSLog(@"WARNING: Did failed to connect to peripheral <%@>  with identifier <%@>", peripheral.name, peripheral.identifier);
}
#pragma mark - Peripheral delegate
- (void) peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
  NSLog(@"INFO: Did discover %d services on %@", (int)[peripheral services].count, [peripheral name]);
  for(CBService *s in [peripheral services]) {
    if([[s UUID] isEqual:findMeServiceUUID])
      immediateAlertService = s;

    if([[s UUID] isEqual:linkLossServiceUUID])
      linkLossService = s;

    NSLog(@"INFO: Did discover service %@ on %@", [s.UUID representativeString], [peripheral name]);
    [peripheral discoverCharacteristics:[NSArray arrayWithObject:alertLevelCharacteristicUUID] forService:s];
  }
}


- (void) peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
  NSLog(@"INFO: Did discover %d characteristics for service %@ on peripheral %@", (int)service.characteristics.count, service, peripheral);
  for(CBCharacteristic *c in [service characteristics])
  {
    NSLog(@"INFO: Did discover characteristic %@ for service %@ on peripheral %@", [c.UUID representativeString], service, peripheral);
    if([service isEqual:immediateAlertService] &&
       [[c UUID] isEqual:alertLevelCharacteristicUUID])
      immediateAlertAlertLevelCharacteristic = c;

    else if([service isEqual:linkLossService] &&
            [[c UUID] isEqual:alertLevelCharacteristicUUID]) {
   
      if(isBonded)
        return;
   
      linkLossAlertLevelCharacteristic = c;
      if(linkLossAlertLevelCharacteristic)
        [self writeLinkLossAlert:BT_ALERT_LEVEL_NONE];
   
    }
  }
}


- (void) peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
  NSLog(@"INFO: Did update value for characteristic %@, new value: %@, error: %@", characteristic, [characteristic value], error);
}


- (void) peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
  if (error)
  {
    NSLog(@"ERROR: Failed to write value for characteristic %@, reason: %@", characteristic, error);
  }
  else
  {
    NSLog(@"INFO: Did write value for characterstic %@, new value: %@", characteristic, [characteristic value]);

    isPairing = NO;
    isBonded = YES;

    [self updateLabel];
  }
}


#pragma mark - UI


-(IBAction)onScanButton:(id)sender {

  if(!isScanning) {
    [self startScan];
    [buttonScan setTitle:@"Stop" forState:UIControlStateNormal];
  }
  else {
    [self stopScan];
    [buttonScan setTitle:@"Start" forState:UIControlStateNormal];
  }
  [self updateLabel];
}


-(IBAction)onConnectButton:(id)sender {
  [self connectPeripheral];
}


-(IBAction)onDisconnectButton:(id)sender {
  [self disconnectPeripheral];
}


-(IBAction)onAlertButton:(id)sender {
  [self alertPeripheral];
}


-(void) updateLabel {
  if(!selectedPeripheral) {
    if(!isScanning)
      [labelPeripheral setText:@"Press start to scan"];
    else
      [labelPeripheral setText:@"Scanning..."];
    return;
  }

  if(hasFailed) {
    [labelPeripheral setText:[NSString stringWithFormat:@"%@ pairing failed", selectedPeripheral.name]];
    return;
  }

  if(isBonded) {
    [labelPeripheral setText:[NSString stringWithFormat:@"%@ bonded", selectedPeripheral.name]];
    return;
  }

  if(isConnected) {
    [labelPeripheral setText:[NSString stringWithFormat:@"%@ connected", selectedPeripheral.name]];
    return;
  }


  if(isPairing) {
    [labelPeripheral setText:[NSString stringWithFormat:@"%@ pairing...", selectedPeripheral.name]];
    return;
  }
}


#pragma Mark - Validating


-(CBPeripheral*)getPeripheral:(NSInteger)arrayIndex {


  CBPeripheral* peripheral = NULL;

  if(!peripherals)
    NSLog(@"FATAL: Peripherals array is NULL");
  else {
    if(peripherals.count < arrayIndex)
      NSLog(@"FATAL: Peripherals array length is too short.");


    peripheral = [peripherals objectAtIndex:arrayIndex];
    if(!peripheral)
      NSLog(@"FATAL: Peripheral is NULL.");
  }

  return peripheral;
}


# pragma mark - TableView thread synchronization


-(void)reloadTable {

  [tablePeripherals performSelectorOnMainThread:@selector(reloadData)
                                     withObject:nil
                                  waitUntilDone:YES];
}


#pragma mark - TableView dataSource and delegate


- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
  return 64;
}


- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
  return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
  if(!peripherals)
    return 0;

  return [peripherals count];
}


- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {


  selectedPeripheral = [self getPeripheral:indexPath.item];
  if(!selectedPeripheral)
    return;

  if(isScanning)
    [self onScanButton:buttonScan];


  [self pairPeripheral];
}


-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {


  UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:@"SettingsCell"];
  if(!cell) {
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"SettingsCell"];
    cell.selectionStyle = UITableViewCellSelectionStyleBlue;
  }

  CBPeripheral* peripheral = [self getPeripheral:indexPath.item];
  if(!peripheral) {
    [cell.textLabel setText:@"<Unexpected error>"];
    return cell;
  }

  [cell.textLabel setText:peripheral.name];
  return cell;
}
@end

Please refer to my answer about this issue on the other thread: https://forums.developer.apple.com/thread/20585