How to Handle BLE Reconnection After iOS Background Termination

Your EEG companion app works flawlessly in the foreground. The user locks their phone, walks around for twenty minutes, and when they come back the app has lost its connection to the headset. Worse, it has no idea the headset is still advertising. This is one of the most common and frustrating problems in BLE-based neurotech products on iOS, and fixing it properly requires understanding exactly how CoreBluetooth behaves when your app is suspended or terminated by the system.

We have spent years building BLE reconnection logic for EEG headsets at DEVSFLOW, including work on the RE-AK Nucleus-Kit and CLEIO devices. This post covers the specific mechanisms iOS provides for background BLE reconnection, the edge cases that will bite you in production, and the strategies that actually work.

How iOS Background Execution Affects BLE

iOS manages app lifecycle aggressively. When your app moves to the background, it gets roughly 30 seconds of continued execution before being suspended. Once suspended, your app's code does not run at all. If the system needs memory, it will terminate your app entirely, removing it from RAM without calling any cleanup methods.

CoreBluetooth has a special relationship with background execution. If your app declares the bluetooth-central background mode in its Info.plist, it can continue scanning and maintaining connections while suspended. But "can" is doing a lot of heavy lifting in that sentence. The reality is more nuanced.

In the background, scanning behavior changes fundamentally. The CBCentralManagerScanOptionAllowDuplicatesKey option is ignored, meaning you receive only one discovery event per peripheral per scan session. Scan intervals are stretched to save power. If the system terminates your app, all active BLE connections are dropped and all pending connect() calls are cancelled.

State Restoration: The Core Mechanism

CoreBluetooth state restoration is Apple's solution for reconnecting after termination. When you initialize your CBCentralManager with a restoration identifier, the system preserves your Bluetooth state even after your app is killed. If a peripheral you were connected to (or trying to connect to) becomes available again, iOS relaunches your app in the background and calls centralManager(_:willRestoreState:).

Setting this up requires two things:

The dictionary keys you care about are CBCentralManagerRestoredStatePeripheralsKey (an array of CBPeripheral objects) and CBCentralManagerRestoredStateScanServicesKey (an array of CBUUID objects). The peripherals in this array are already in a connected or connecting state. You need to set their delegates immediately and resume your data pipeline.

Peripheral Identifier Caching

Every CBPeripheral has a identifier property, which is a UUID that iOS assigns and keeps stable for a given peripheral on a given iOS device. This is not the peripheral's MAC address. It is a local identifier that persists across app launches and even across Bluetooth resets, as long as the peripheral's identity resolving key (IRK) has not changed.

You should cache this identifier in UserDefaults or your local database immediately upon first successful connection. When your app launches (or relaunches via state restoration), use centralManager.retrievePeripherals(withIdentifiers:) to get a reference to the peripheral object without scanning. This is significantly faster than a full scan and works even when the peripheral is not currently advertising.

If retrievePeripherals returns your device, call connect() on it. CoreBluetooth will queue this connection request and fulfill it whenever the peripheral becomes available, even if that is minutes or hours later. This pending connection survives app suspension but not app termination, which is why state restoration exists.

The Reconnection Strategy That Works

After extensive testing with EEG headsets, we use a layered reconnection approach:

  1. On centralManagerDidUpdateState with .poweredOn, immediately call retrievePeripherals(withIdentifiers:) with cached identifiers. If a peripheral is returned, call connect().
  2. If retrievePeripherals returns nothing, call retrieveConnectedPeripherals(withServices:) using your EEG service UUID. This catches cases where another app or the system has an active connection to the headset.
  3. If neither retrieval method works, start a filtered scan for your service UUID. In the background, this scan is throttled but still functional.
  4. If state restoration fires via willRestoreState, skip steps 1 through 3. The peripherals are already provided. Set delegates and proceed.

Each of these steps feeds into the same connection pipeline. Once didConnect fires, discover services, discover characteristics, enable notifications, and resume your data stream. The entire pipeline must be idempotent because you cannot predict which entry point will fire.

Edge Cases with EEG Headsets

Consumer EEG headsets introduce several complications that generic BLE reconnection guides do not cover:

Testing Background Termination

You cannot reliably test state restoration by force-quitting the app from the app switcher. Force-quitting via the app switcher explicitly disables state restoration and background relaunch. The user has expressed intent to kill the app, and iOS respects that.

To test system-initiated termination, you need to simulate memory pressure. The most reliable method is to open many memory-intensive apps while your BLE app is in the background, forcing iOS to terminate it. Alternatively, you can use Xcode's Debug menu to simulate a memory warning, though this does not always trigger actual termination.

We maintain a test harness that logs every state transition: suspension, termination, restoration, and reconnection timestamps. After each build, we run a test cycle where the app connects to a headset, moves to background, gets terminated via memory pressure, and must reconnect within 10 seconds of the headset becoming available again. Any build that fails this cycle does not ship.

Common Mistakes

Three mistakes account for most of the BLE reconnection bugs we see in neurotech apps:

BLE reconnection on iOS is one of the trickiest parts of building a reliable neurotech companion app. If your team is struggling with background BLE behavior for EEG or biometric devices, talk to DEVSFLOW Neuro. We build mobile apps that maintain rock-solid connections to headsets and wearables.