title: iOS Bluetooth Low Energy ANCS apparent '3 strikes and you're out'
body: <p>We have a BLE peripheral that subscribes to ANCS. It also hosts some custom services to which our app connects.</p>
  
  <p>Because of ANCS, connection to the peripheral (xxx) has to be made in the Settings app.
  So we do not have any facility from within the app to initiate a BLE connection.
  This also keeps the UseEx cleaner, and reduces 'corner cases'.</p>
  
  <p>The idea being that the user connects in the Settings app.
  Then if the peripheral goes out of range, iOS will automatically re-connect once back in range.</p>
  
  <p>However the client found that often the peripheral would not reconnect after say being out of the room.</p>
  
  <p>We can only reliably reproduce this by placing the BLE peripheral inside a metal box at approx 15 metres distance from the iPhone. By opening and closing the case at just the right speed, we can induce a series of failed ANCS connection requests within a 10 second or so timeframe.</p>
  
  <p>Here are some device console logs showing the first failed connection due to signal loss.</p>
  
  <pre><code>Mar 18 16:38:05 afes-iPhone BTLEServer[107] &lt;Notice&gt;: (Note ) Central "xxx" is now subscribed to characteristic "ANCS Data Source"
  Mar 18 16:38:07 afes-iPhone BTServer[68] &lt;Error&gt;: Core       Connection timed out to device "xxxxxxx"
  Mar 18 16:38:07 afes-iPhone BTLEServer[107] &lt;Notice&gt;: (Note ) Peripheral "xxx" is now disconnected: Error Domain=CBErrorDomain Code=6 "The connection has timed out unexpectedly." UserInfo=0x14528470 {NSLocalizedDescription=The connection has timed out unexpectedly.}
  Mar 18 16:38:07 afes-iPhone BTLEServer[107] &lt;Notice&gt;: (Note ) Connecting peripheral "xxx"...
  Mar 18 16:38:07 afes-iPhone BTLEServer[107] &lt;Notice&gt;: (Note ) Central "xxx" is now unsubscribed from characteristic "ANCS Notification Source"
  Mar 18 16:38:07 afes-iPhone BTLEServer[107] &lt;Notice&gt;: (Note ) Central "xxx" is now unsubscribed from characteristic "ANCS Data Source"
  Mar 18 16:38:08 afes-iPhone BTLEServer[107] &lt;Notice&gt;: (Note ) Peripheral "xxx" is now connected
  Mar 18 16:38:08 afes-iPhone Preferences[164] &lt;Warning&gt;: (Warn ) CoreBluetooth: API MISUSE: &lt;CBPeripheral: 0x14da8170, identifier = xxxxxx, name = xxx, state = disconnected&gt; can only accept commands while in the connected state
  ....
  Mar 18 16:38:09 afes-iPhone BTServer[68] &lt;Error&gt;: Core       Connection timed out to device "xxxxxxxxx"
  Mar 18 16:38:09 afes-iPhone BTLEServer[107] &lt;Notice&gt;: (Note ) Peripheral "xxx" is now disconnected: Error Domain=CBErrorDomain Code=6 "The connection has timed out unexpectedly." UserInfo=0x1453daf0 {NSLocalizedDescription=The connection has timed out unexpectedly.}
  Mar 18 16:38:09 afes-iPhone BTLEServer[107] &lt;Notice&gt;: (Note ) Connecting peripheral "xxx"...
  Mar 18 16:38:09 afes-iPhone BTLEServer[107] &lt;Notice&gt;: (Note ) Peripheral "xxx" did not use any built-in service, strike #1
  </code></pre>
  
  <p>...</p>
  
  <p>And if we continue with just the right opening and closing of our metal box we can effect the same 'API MISUSE' three times in a row.</p>
  
  <p>Resulting in the following messages:</p>
  
  <pre><code>Mar 18 16:38:12 afes-iPhone BTLEServer[107] &lt;Notice&gt;: (Note ) Peripheral "xxx" did not use any built-in service, strike #2
  </code></pre>
  
  <p>...</p>
  
  <pre><code>Mar 18 16:38:16 afes-iPhone BTLEServer[107] &lt;Notice&gt;: (Note ) Peripheral "xxx" did not use any built-in service, strike #3
  </code></pre>
  
  <p>...</p>
  
  <p>It seems this is a 'three strikes and you're out' algo that then places the peripheral into some kind of 'grey-list'. The time has to be within the 11 or so seconds range.
  We can't slow down our interrogations or we go up against the 30 second key exchange with pairing time outs.</p>
  
  <p>This grey list means that iOS will no longer allow a re-connection of this peripheral to ANCS without the user opening Settings and manually connecting to it.</p>
  
  <p>Which means that the CBCentralManager
  - retrieveConnectedPeripheralsWithServices:
  method will not retrieve the peripheral because it is no longer connected.</p>
  
  <p>We have had to add a work-around whereby we now store the UDID of any peripherals previously seen, then upon a callback to didDisconnectPeripheral we reconnect using
  - retrievePeripheralsWithIdentifiers:</p>
  
  <p>However, to maintain continuity of ANCS connection, we have also had to add bluetooth-central UIBackground mode to Info.plist so that reconnection is made upon receipt of didDisconnectPeripheral with the app in the background. Of course if the app is unloaded then without state preservation and restoration, the ANCS connection will again be dropped.</p>
  
  <p>For good measure we are also utilising a watchdog timer to periodically check for connection state, when in foreground.</p>
  
  <p>We can only reproduce the effect with difficulty, our client seems to be able to see it very often, it is certainly true that connection unreliability is likely for BLE devices.</p>
  
  <p>We could make our app much more power efficient and develop it with much less effort if ANCS was handled more robustly in iOS.</p>
  
  <p>Devices used are iPhone 4S and iPhone 6 both on iOS 8.1 and 8.2. </p>
  
  <p>Question is : Has anyone else seen this behaviour with ANCS ?</p>