title: How to stop ios core-bluetooth CBCentralManagerDelegate from disconnecting in ios13
body: <h2>The Issue</h2>
  
  <p>I'm using core-bluetooth to transmit sensor data (an array of 64-128 bytes) to an iPad (6th gen) at a rate of 6 Hz. It was working fine in ios 12. After I updated to ios 13, the bluetooth connection became incredibly unreliable. The connection would only transmit 20 bytes at a time, and it would frequently disconnect (<code>centralManager(_ central: CBCentralManager, didDisconnectPeripheral...</code> is called) with Error <code>Code=0 "Unknown error." UserInfo={NSLocalizedDescription=Unknown error.}</code>.</p>
  
  <p>Looking at the iPad screen during debugging, I noticed every time the central connects and my service is discovered, a security prompt saying "<code>Bluetooth Pairing Request: "&lt;device-name&gt;" would like to pair with your iPad. [Cancel] [Pair]</code>" pops up for half a second before the disconnect described above.</p>
  
  <p>So it would seem that for some reason, core bluetooth is triggering a security prompt and (i'm guessing) the delay causes the connection to fail? The strange thing is this prompt happens like 19/20 times, but occasionally the connection goes through without triggering the prompt and a couple buffers are received before disconnecting.</p>
  
  <h1>Related Problems</h1>
  
  <p>There is <a href="https://stackoverflow.com/questions/57851959/how-to-control-when-to-present-the-bluetooth-permission-request-prompt-in-ios13">another post</a> about something similar: how to prevent the permission request in ios 13, but it looks like this is more due to the peripheral based on the error message.</p>
  
  <p>There is <a href="https://stackoverflow.com/questions/57621445/cbcentralmanagerdelegate-diddiscoverperipheral-advertisementdata-in-ios-13-do">a post</a> detailing a missing value in the <code>CBCentralManager didDiscover peripheral</code> dictionary, but I am not using that value anyway. </p>
  
  <p>I am definitely storing a hard-reference to the peripheral, so that is not the issue here. I have also opened other apps that allow you to browse bluetooth4.0 peripheral gatt servers. LightBlue, BLE Scanner, and BLE Hero all exhibit the bluetooth connection prompt followed by a disconnect from the peripheral on my iPad 6th gen.</p>
  
  <h1>My Implementation</h1>
  
  <p>This project is part of a school class. I have all of the code on gitlab. <a href="https://gitlab.com/alecGraves/j5-ios-app/blob/master/BLEDataViewer/BLECentral.swift" rel="nofollow noreferrer">The critical ios bluetooth code is below, and the project is on gitlab</a>.</p>
  
  <pre class="lang-cpp prettyprint-override"><code>
  import Foundation
  import CoreBluetooth
  
  struct Peripheral {
      var uuid: String
      var name: String
      var rssi: Double
      var peripheral: CBPeripheral
  }
  
  class SensorsComputer: BLECentral {
      // just a rename of below...
  }
  
  class BLECentral: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate {
      var manager: CBCentralManager!
      var availablePeripherals: [String: Peripheral]
      var sensorsConnected: Bool
      var ref: CBPeripheral?
      var sensorServiceUUIDs: [String]
      var bleSensorComputerUUID: String
      var data: [String:[UInt8]]
      var dataNames: [String:[String:String]]
  
      override init() {
          data = [:]
          availablePeripherals = [:]
          sensorsConnected = false
          ref = nil
          sensorServiceUUIDs = ["5CA4"]
          //bleSensorComputerUUID = "096A1CFA-C6BC-A00C-5A68-3227F2C58C06" // builtin is shit
          bleSensorComputerUUID = "33548EF4-EDDE-6E6D-002F-DEEFC0A7AF99" // usb dongle
  
          dataNames = [
              // service uuids
              "5CA4": [
                  // characteristic uuids
                  "0000": "LidarRanges",
              ],
          ]
  
          super.init()
          manager = CBCentralManager(delegate: self, queue: nil)
          self.connect(uuid:bleSensorComputerUUID)
      }
  
      func scan(){
          let services = dataNames.map { CBUUID(string:$0.key) }
          manager.scanForPeripherals(withServices: services, options: nil)
      }
  
      func connect_internal_(uuid: String) -&gt; Bool{ // TODO known bt device uuids.
          if self.sensorsConnected {
              // do not try to connect if already connected
              return true
          } else {
              print(self.availablePeripherals.count)
              if let found = self.availablePeripherals[uuid] {
                  manager.stopScan()
                  manager.connect(found.peripheral, options: nil)
                  return true
              } else {
                  if availablePeripherals.count == 0 {
                      scan()
                  }
                  print("Error! no peripheral with \(uuid) found!")
                  return false
              }
          }
      }
  
      func connect(uuid: String){
          let queue = OperationQueue()
          queue.addOperation() {
              // do something in the background
              while true {
                  //usleep(100000)
                  sleep(1)
                  OperationQueue.main.addOperation {
                      self.connect_internal_(uuid: self.bleSensorComputerUUID)
                  }
              }
          }
      }
  
      func centralManagerDidUpdateState(_ central: CBCentralManager) {
          switch central.state {
          case .poweredOn:
              print("The central is powered on!")
              scan() // automatically start scanning for BLE devices
          default:
              print("The centraol is NOT powered on (state is \(central.state))")
          }
      }
  
      func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
          var name = ""
          if let name_ = peripheral.name {
              name = name_
          }
  
          let uuid = peripheral.identifier.uuidString
          let rssi = Double(truncating: RSSI)
          availablePeripherals[peripheral.identifier.uuidString] = Peripheral(uuid: uuid, name: name, rssi: rssi, peripheral: peripheral)
          print(uuid, rssi)
      }
  
      func getSorted(uuids:Bool = false) -&gt; [Peripheral] {
          let peripherals = Array(availablePeripherals.values)
          return peripherals.sorted(by:) {$0.rssi &gt;= $1.rssi}
      }
  
      func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
          print("Central connected!")
          sensorsConnected = true
          peripheral.delegate = self
          var cbuuids:[CBUUID] = []
          for id in sensorServiceUUIDs {
              cbuuids.append(CBUUID(string: id))
          }
          peripheral.discoverServices(cbuuids) // TODO store service uuids somewhere nicer
      }
  
      func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
          if let e = error {
              print("Central disconnected because \(e)")
          } else {
              print("Central disconnected! (no error)")
          }
          sensorsConnected = false
          availablePeripherals = [:]
      }
  
      func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
          print("Central failed to connect...")
      }
  
      func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
          if let error = error {
              print("Peripheral could not discover services! Because: \(error.localizedDescription)")
          } else {
              peripheral.services?.forEach({(service) in
                  print("Service discovered \(service)")
                  // TODO characteristics UUIDs known
                  peripheral.discoverCharacteristics(nil, for: service)
              })
          }
      }
  
      func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
          if let error = error {
              print("Could not discover characteristic because error: \(error.localizedDescription)")
          } else {
              service.characteristics?.forEach({ (characteristic) in
                  print("Characteristic: \(characteristic)")
                  if characteristic.properties.contains(.notify){
                      peripheral.setNotifyValue(true, for: characteristic)
                  }
                  if characteristic.properties.contains(.read){
                      peripheral.readValue(for: characteristic)
                  }
                  if characteristic.properties.contains(.write){
                      peripheral.writeValue(Data([1]), for: characteristic, type: .withResponse)
                  }
              })
  
          }
      }
  
  
      func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
          if let error = error {
              print("error after didUpdateValueFor characteristic \(characteristic): \(error.localizedDescription)")
          } else {
              let sname = characteristic.service.uuid.uuidString
              let cname = characteristic.uuid.uuidString
              guard let _dname = dataNames[sname], let dataName = _dname[cname] else {
                  return
              }
              //print("value of characteristic \(cname) in service \(sname) was updated.")
              if let value = characteristic.value {
                  let value_arr = Array(value)
                  //print("    The new data value is \(value_arr.count) bytes long")
                  //print("dataname is \(dataName)")
                  self.data[dataName] = value_arr
              }
          }
      }
  }
  
  </code></pre>
  
  <p>The BLE peripheral code is a node project based on bleno and rosnodejs for sensor data. <a href="https://gitlab.com/alecGraves/j5/tree/master/ros/bluetooth_bridge/node_bt_bridge" rel="nofollow noreferrer">The peripheral code is also on gitlab</a>.</p>
  
  <p>I have rolled back to several known working project versions in git, and nothing appears to work after updating to ios 13. I am going to try to run the corebluetooth code on a mac project to test if it will work there.</p>
  
  <h2>Question</h2>
  
  <p>So, in conclusion, after updating to ios 13 from ios 12.4, when connecting to a BLE peripheral, a bluetooth connection prompt is opened even though the communication is bluetooth 4.0 / BLE. Along with this prompt, the connection drops as soon as it starts, sometimes giving me a few partial data buffers and sometimes giving me nothing.</p>
  
  <p>The BLE connection not properly initiated, and if the prompt for connection did work correctly, it would interfere with my ability to connect to a device (robot sensors) automatically without the user input.</p>
  
  <p>If anyone knows some option in info.plist to prevent this breaking connection prompt from appearing, that would be great. I am at a loss here, is there an easy way to roll-back ios versions? Or some way to get Apple INC to release an emergency patch preventing the popup for core-bluetooth low-energy? Or some way to work around the popup and allow the user to properly authenticate without breaking BLE protocol?</p>
  
  <p>Thanks to anyone who takes a look at this.</p>