iosswiftbackgroundcore-bluetoothscanning

Corebluetooth Scanning BGAppRefreshTask not working


I am working on a project for scanning BLE devices all around in the foreground and there is no issue. It is working well! But background I cannot make scanning. I did everything on info.plist and on the section "Signing&Capabilities", switch on the Background Model for "Background Fetch". I read almost everything on the Web about Corebluetooth, BGAppRefreshTask, Background Modes but find nothing.

Only thing I find get advertisement data of the BLE devices around me. UUID and other things are not important for me. I only need to identify BLE devices and know that it is same in other iOS device when it also look for it.

If you have any other solution to guide me to go another way, it will appreciated.

Here is the code of BLECentral. The .swift file which I want to call in Background. import Foundation import CoreBluetooth

class BLECentral: NSObject, CBCentralManagerDelegate {
    
    var manager: CBCentralManager!
    var discoveredPeripherals = [DiscoveredPeripheral]()
    var onDiscovered: (()->Void)?
    
    override init() {
        super.init()
        manager = CBCentralManager(delegate: self, queue: nil)
    }
    
    func scanForPeripherals() {
        let options: [String: Any] = [CBCentralManagerScanOptionAllowDuplicatesKey: false]
        manager.scanForPeripherals(withServices: nil, options: options)
        print("ben tarıyom")
    }
    
    // MARK: - CBCentralManagerDelegate
    
    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        if central.state == .poweredOn {
            scanForPeripherals()
        } else {
            print("central is unavailable: \(central.state.rawValue)")
        }
    }
    
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
        if let existingPeripheral = discoveredPeripherals.first(where: {$0.peripheral == peripheral}) {
            existingPeripheral.advertisementData = advertisementData
            existingPeripheral.rssi = RSSI

            print(existingPeripheral.advertisementData)
            

        } else {
            discoveredPeripherals.append(DiscoveredPeripheral(peripheral: peripheral, rssi: RSSI, advertisementData: advertisementData))
        }
        onDiscovered?()
    }
}

Here is the AppDelegate code:

import UIKit
import CoreBluetooth
import BackgroundTasks

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    var central: BLECentral!

    var manager: CBCentralManager!
    var window: UIWindow?
    var flowController: AppFlowController?
    


    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        
        
        window = UIWindow(frame: UIScreen.main.bounds)
        flowController = AppFlowController(window: window!)
        flowController?.start()
        
        BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.blebacktrial.refresh", using: nil) { (task) in
            self.handleAppRefresh(task: task as! BGAppRefreshTask)
        }
        
        
        return true
    }

    func handleAppRefresh(task: BGAppRefreshTask) {
        
        central.scanForPeripherals()
        
        scheduleAppRefresh()

    }
    

    
    // MARK: - Scheduling Tasks
    
    func scheduleAppRefresh() {
        let request = BGAppRefreshTaskRequest(identifier: "com.blebacktrial.refresh")
        request.earliestBeginDate = Date(timeIntervalSinceNow: 60) // Fetch no earlier than 1 minutes from now
        
        do {
            try BGTaskScheduler.shared.submit(request)
            print("task scheduled")
        } catch {
            print("Could not schedule app refresh: \(error)")
        }
    }
    
    
    // MARK: UISceneSession Lifecycle

    func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {

        return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
    }

    func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {

    }


}

Solution

  • You don't need to use a background refresh task. Scanning for BLE peripherals is a supported background mode in its own right.

    However, you can't scan for service nil in the background. You must scan for the specific service that you are interested in.

    From the documentation

    Your app can scan for Bluetooth devices in the background by specifying the bluetooth-central background mode. To do this, your app must explicitly scan for one or more services by specifying them in the serviceUUIDs parameter. The CBCentralManager scan option has no effect while scanning in the background.