title: iOS 11 Core Bluetooth restoration not working
body: <p>I have an iOS app that uses Core Bluetooth to connect to a V.BTTN smart button. Everything worked perfectly fine in iOS 10, but since iOS 11 has come out the restoration process seems to break.</p>
  
  <p>When I launch my app, with a previously paired button, the OS does in fact call the <code>centralManager:willRestoreState</code>, and the accompanying dictionary includes a pointer to a CBPeripheral object that has a status of connected. Just as it did back in iOS 10. However, the problem I am running into is when I call the <code>discoverServices</code> on the peripheral I am never returned any services in the <code>peripheral:didDiscoverServices</code> method. In fact that method, nor any other method, is called at all.</p>
  
  <p>I have been searching all of the internet and have found people having similar issues with button pairing an connections, but those have generally been issues with improper lifecycle management of the peripheral object. I believe I have everything setup correct and at a loss. Does anyone have any idea what could be going on here?</p>
  
  <pre><code>import Foundation
  import CoreBluetooth
  
  class SmartButtonManager: NSObject {
  
      //MARK: - Singleton
  
      static let sharedManager = SmartButtonManager()
  
      //MARK: - Properties
  
      var discoveredDevices: [CBPeripheral] {
          get {
              return perfs
          }
      }
  
      fileprivate(set) var isBluetoothOn = false
  
      //MARK: - Private Constants &amp; Variables
  
      fileprivate var connectedButtonUUID: String?
  
      fileprivate let queue = DispatchQueue(label: "V.BTTN", qos: .background, attributes: .concurrent, autoreleaseFrequency: .inherit, target: nil)
  
      fileprivate var manager: CBCentralManager?
  
      fileprivate var perfs = [CBPeripheral]()
      fileprivate var connectedPerf: CBPeripheral?
  
      fileprivate var isButtonReady = false
  
      //MARK: - Initialization
  
      override init() {
          super.init()
      }
  
      //MARK: - Configure
  
      func configure(withLaunchOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) {
  
          // create new central manager
          manager = CBCentralManager(delegate: self, queue: queue, options: [CBCentralManagerOptionRestoreIdentifierKey: managerIdentifier])
      }
  }
  
  //MARK: - V.BTTN
  
  extension SmartButtonManager: CBCentralManagerDelegate, CBPeripheralDelegate {
  
      func scanForVbttn() {
  
          perfs.removeAll()
          manager?.scanForPeripherals(withServices: services, options: nil)
      }
  
      func centralManagerDidUpdateState(_ central: CBCentralManager) {
  
          switch central.state {
  
          case .poweredOff:
              print("[centralManagerDidUpdateState] CB BLE hardware is powered off")
              perfs.removeAll()
              isBluetoothOn = false
  
          case .poweredOn:
              print("[centralManagerDidUpdateState] CB BLE hardware is powered on. Start scanning for peripherals")
              isBluetoothOn = true
  
          case .unauthorized:
              print("[centralManagerDidUpdateState] CB BLE hardware is not authorized")
  
          case .unsupported:
              print("[centralManagerDidUpdateState] CB BLE hardware is not supported")
              isBluetoothOn = false
  
          default:
              print("[centralManagerDidUpdateState] CB BLE hardware state is unknown")
          }
      }
  
      func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
  
          print("[centralManager:didDiscover peripheral:advertisementData] CB BLE did discover peripheral with advertisement data: \(advertisementData)")
  
          guard let perfName = advertisementData[CBAdvertisementDataLocalNameKey] as? String else {
              print("[centralManager:didDiscover peripheral:advertisementData] peripheral name is unknown")
              return
          }
  
          if perfName.contains("V.ALRT") {
  
              peripheral.delegate = self
              perfs.append(peripheral)
  
              if connectedButtonUUID == perfName {
                  connect(peripheral: peripheral)
              }
          }
      }
  
      func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
  
          print("[central:didConnect peripheral:] CB BLE hardware did connect")
          handleDidConnect(toPeripheral: peripheral)
      }
  
      func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
  
          print("[central:didFailToConnect error:] CB BLE peripheral did failed to connect with error: \(String(describing: error))")
          connectedPerf = nil
          isButtonReady = false
          connectedButtonUUID = nil
      }
  
      func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
  
          print("[central:didDisconnectPeripheral peripheral:] CB BLE peripheral did disconnect")
          connectedPerf = nil
          isButtonReady = false
          connectedButtonUUID = nil
      }
  
      func centralManager(_ central: CBCentralManager, willRestoreState dict: [String : Any]) {
  
          print("[central:willRestoreState dict:] CB BLE hardware will restore state")
          print("\(dict)")
  
          guard let ps = dict[CBCentralManagerRestoredStatePeripheralsKey] as? [CBPeripheral] else {
              print("[central:willRestoreState dict:] No perfs to restore")
              return
          }
  
          print("[central:willRestoreState dict:] Will restore perfs")
  
          perfs = ps
  
          print("[central:willRestoreState dict:] Attempt to reconnect to V.BTTN")
          print("[central:willRestoreState dict:] perfs \(perfs)")
  
          for p in perfs {
              if p.name == connectedButtonUUID {
  
                  print("[central:willRestoreState dict:] Connect to perf \(p)")
                  handleDidConnect(toPeripheral: p)
                  break
              }
          }
      }
  
      func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
  
          guard let services = peripheral.services else {
              print("[peripheral:didDiscoverServices error:] CB BLE peripheral unable to discover services")
              return
          }
  
          print("[peripheral:didDiscoverServices error:] BLE peripheral did discover services")
  
          for s in services {
              print("[peripheral:didDiscoverServices error:] CB BLE Service \(s.description)")
              peripheral.discoverCharacteristics(nil, for: s)
          }
      }
  
      func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
  
          print("[peripheral:didDiscoverCharacteristicsFor service:] CB BLE did discover characteristics for service \(service.uuid.description)")
  
          if compareCBUUID(uuid1: service.uuid, uuid2: CBUUID(string: BLE_VSN_GATT_SERVICE_UUID)) {
  
              guard let characteristics = service.characteristics else {
                  return
              }
  
              for aChar in characteristics {
  
                  // write the verification key
                  if aChar.uuid.isEqual(CBUUID(string: BLE_VERIFICATION_SERVICE_UUID)) {
  
                      self.writeVerificationKey(forPeripheral: peripheral, characteristic: aChar)
                      self.enable(peripheral: peripheral)
  
                      break
                  }
              }
          }
      }
  
      func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
  
          //NOTE: This code has been omitted as there is little need to see this
      }
  
      func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
  
          print("[peripheral:didWriteValueFor characteristic:] characteristic: \(characteristic.uuid)")
  
          if let e = error {
              print("[peripheral:didWriteValueFor characteristic:] error: \(e)")
          }
      }
  
      func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
  
          if !characteristic.isNotifying {
              print("[peripheral:didUpdateNotificationStateFor:][Characteristic is not notifiying so cancel the connection]")
              manager?.cancelPeripheralConnection(peripheral)
          }
      }
  
      //MARK: - Helpers
  
      fileprivate func writeVerificationKey(forPeripheral peripheral: CBPeripheral, characteristic: CBCharacteristic) {
  
          print("[peripheral:didDiscoverCharacteristicsFor service:] Write verification key")
          let data = NSData(bytes: [0x80,0xBE,0xF5,0xAC,0xFF] as [UInt8], length: 5)
          peripheral.writeValue(data as Data, for: characteristic, type: CBCharacteristicWriteType.withResponse)
      }
  
      fileprivate func handleDidConnect(toPeripheral peripheral: CBPeripheral) {
  
          if let n = peripheral.name {
              connectedButtonUUID = n
          }
  
          connectedPerf = peripheral
  
          peripheral.delegate = self
          peripheral.discoverServices(nil)
          isButtonReady = true
      }
  
      fileprivate func enable(peripheral: CBPeripheral) {
  
          guard let services = peripheral.services else {
              return
          }
  
          for service: CBService in services {
  
              if compareCBUUID(uuid1: service.uuid, uuid2: CBUUID(string: BLE_VSN_GATT_SERVICE_UUID)) {
  
                  guard let characteristics = service.characteristics else {
                      return
                  }
  
                  for aChar in characteristics {
  
                      if aChar.uuid.isEqual(CBUUID(string: BLE_KEYPRESS_DETECTION_UUID)) {
  
                          // enable button
                          print("[peripheral:didDiscoverCharacteristicsFor service:] Enable short press")
  
                          let data = NSData(bytes: [0x02] as [UInt8], length: 1)
                          peripheral.writeValue(data as Data, for: aChar, type: CBCharacteristicWriteType.withResponse);
  
                      } else if aChar.uuid.isEqual(CBUUID(string: BLE_SILENT_NORMAL_MODE)) {
  
                          // enable button
                          print("[peripheral:didDiscoverCharacteristicsFor service:] Enable normal mode")
  
                          let data = NSData(bytes: [0x00] as [UInt8], length: 1)
                          peripheral.writeValue(data as Data, for: aChar, type: CBCharacteristicWriteType.withResponse);
  
                      } else if aChar.uuid.isEqual(CBUUID(string: BLE_FALL_KEYPRESS_DETECTION_UUID)) {
  
                          // enable fall detection
                          print("[peripheral:didDiscoverCharacteristicsFor service:] Enable fall detection")
                          peripheral.setNotifyValue(true, for: aChar)
                      }
                  }
              }
          }
  
          checkBatteryLevel()
      }
  
      func disconnectVbttn() {
  
          guard let peripheral = connectedPerf else {
              return
          }
  
          setVbttnToStealthMode()
          manager?.cancelPeripheralConnection(peripheral)
          isButtonReady = false
      }
  
      fileprivate func compareCBUUID(uuid1: CBUUID, uuid2: CBUUID) -&gt; Bool {
  
          if (uuid1.data as NSData).isEqual(to: uuid2.data) {
              return true
          }
  
          return false
      }
  
      func stopScanning() {
          manager?.stopScan()
      }
  }
  </code></pre>