Detecting a backgrounded iOS-Peripheral

We are developing an app where we want iPhone to iPhone communication via bluetooth. We are using the core bluetooth API. One iPhone acts as the peripheral and the other as the central. When both apps are on screen they successfully find each other and can connect. However when the peripheral is in background the central can't find it. To be clear the central is still in foreground. I have consulted this page: https://developer.apple.com/library/archive/documentation/NetworkingInternetWeb/Conceptual/CoreBluetooth_concepts/CoreBluetoothBackgroundProcessingForIOSApps/PerformingTasksWhileYourAppIsInTheBackground.html and other resources. From my understanding the service UUID should be moved to the manufacturer data. This makes it difficult for non-iOS devices to detect the service but the documentation says that my central should be able to find the peripheral. I have added the required keys to the pList and am explicitly searching for the mentioned service UUID. When using a third party BLE Tool I'm able to find and communicate with the peripheral even when it is in background mode. I know that some option-keys will be ignored but from my understanding they should still find each other at least once. This was tested with two iOS 18 iPhones. Here is the code im using:

import UIKit
import CoreBluetooth

class ViewController: UIViewController {
    private var centralManager: CBCentralManager!
    private var peripheralManager: CBPeripheralManager!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        

    }

    @IBAction func btnPeripheral(_ sender: Any) {
        peripheralManager = CBPeripheralManager(delegate: self, queue: nil, options:  nil)
    }
    
    @IBAction func btnCentral(_ sender: Any) {
        print("central go")
        centralManager = CBCentralManager(delegate: self, queue: nil)
    }
}

extension ViewController: CBCentralManagerDelegate {
    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        print("central state: \(central.state)")
        switch central.state {
        case .poweredOn:
            print("central powered on")
            centralManager.scanForPeripherals(withServices: [CBUUID(string:"db9acb1e-1ac4-4f70-b58c-3b3dcea84703")], options: [CBCentralManagerScanOptionAllowDuplicatesKey: true])
        default:
            print("central not powered on")
        }
    }
    
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
        print("Dicovered \(peripheral)")
    }
}

extension ViewController: CBPeripheralManagerDelegate {
    func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
        print("peripheral state: \(peripheral.state)")
        if peripheral.state == .poweredOn {
            print("powered on")
            let uuid = CBUUID(string: "db9acb1e-1ac4-4f70-b58c-3b3dcea84703")
            let service = CBMutableService(type: uuid, primary: true)
            peripheralManager.add(service)
            peripheralManager.startAdvertising([CBAdvertisementDataServiceUUIDsKey: [uuid], CBAdvertisementDataLocalNameKey: "MyService"])
        }
    }
}

I've had the same problem since I updated to iOS 18. This does work when the central running the scan is on iOS < 18 (even if the peripheral is iOS 18).

If you want to receive callback notifications on receipt of advertisements from a backgrounded iOS peripheral on iOS 18 - it seems you have to remove the ServiceUUID filter and deal with the noise. Unhelpfully - iOS removes any manufacturer data with the Apple manufacturer code 004c - so you can't use that to check for the service. I've been caching device identifiers which I've previously seen advertising the service as a workaround - but this isn't great as these change after a while, and it requires the peripheral to have been foregrounded at some point whilst the central was scanning.

Really hoping this will be fixed - it's certainly not the behaviour described in the API documentation.

Hi,

I think here is also a discussion about that topic. Maybe someone has additional information?

Apple Developer Forum Thread 759280

Thank you!

Detecting a backgrounded iOS-Peripheral
 
 
Q