1

I created a beacon simulator app that advertises the beacon in foreground mode, the app runs in background mode but it does not advertise the beacon.
 Here is my working code

import UIKit
import CoreBluetooth
import CoreLocation

class ViewController: UIViewController,CBPeripheralManagerDelegate, CLLocationManagerDelegate {
    @IBOutlet weak var major: UILabel!
    @IBOutlet weak var minor: UILabel!
    @IBOutlet weak var uuid: UILabel!
    
    var locationManager: CLLocationManager!
    var localBeacon: CLBeaconRegion!
    var beaconPeripheralData: NSDictionary!
    var peripheralManager: CBPeripheralManager!
    let localBeaconUUID = "88b78a0c-34ae-44d0-b30c-001F12345620"
    let localBeaconMajor: CLBeaconMajorValue = 58
    let localBeaconMinor: CLBeaconMinorValue = 20
    let identifier = "myBeacon"
    let txPower = -40
    var timer: Timer!
    
    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        switch central.state {
        case .unknown:
            print("central.state is .unknown")
        case .resetting:
            print("central.state is .resetting")
            
        case .unsupported:
            print("central.state is .unsupported")
            
        case .unauthorized:
            print("central.state is .unauthorized")
            
        case .poweredOff:
            print("central.state is .poweredOff")
            
        case .poweredOn:
            print("central.state is .poweredOn")
            
            peripheralManager.startAdvertising(beaconPeripheralData as? [String: Any] )
            
        }
        
    }
    
    
    
    @objc func initLocalBeacon() {
        if localBeacon != nil {
            stopLocalBeacon()
        }
        let uuid = UUID(uuidString: localBeaconUUID)!
        localBeacon = CLBeaconRegion(proximityUUID: uuid, major: localBeaconMajor, minor: localBeaconMinor, identifier: identifier)
        locationManager = CLLocationManager()
        locationManager.delegate = self
        locationManager.requestAlwaysAuthorization()
        locationManager.allowsBackgroundLocationUpdates = true
        
        beaconPeripheralData = localBeacon.peripheralData(withMeasuredPower:nil)
        peripheralManager = CBPeripheralManager(delegate: self, queue: nil, options: nil)
    }
    func stopLocalBeacon() {
        peripheralManager.stopAdvertising()
        peripheralManager = nil
        beaconPeripheralData = nil
        localBeacon = nil
    }
    
    
    func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
        if peripheral.state == .poweredOn {
            
            peripheralManager.startAdvertising(beaconPeripheralData as? [String: Any] )
        }
        else if peripheral.state == .poweredOff {
            peripheralManager.stopAdvertising()
        }
    }
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        major.text = "\(localBeaconMajor)"
        minor.text = "\(localBeaconMinor)"
        uuid.text = "\(localBeaconUUID)"
        
        self.timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(initLocalBeacon), userInfo: nil, repeats: true)
        
    }
   
    
    
    func peripheralManager(_ peripheral: CBPeripheralManager, willRestoreState dict: [String : Any]) {
        if let services = dict[CBPeripheralManagerRestoredStateServicesKey] as? [CBMutableService],
           let service = services.first {
            peripheral.add(service)
        }
    }
    
    func peripheralManager(_ peripheral: CBPeripheralManager, didAdd service: CBService, error: Error?) {
        if let error = error {
            print("Error advertising service: \(error.localizedDescription)")
            return
        }
        
        let power = NSNumber(integerLiteral: -59)
        beaconPeripheralData = localBeacon.peripheralData(withMeasuredPower: power)
        
        peripheralManager.startAdvertising(beaconPeripheralData as! [String: Any])
    }
    
    func applicationDidEnterBackground(_ application: UIApplication) {
        print("background")
        peripheralManager.stopAdvertising()
        let backgroundTask = UIApplication.shared.beginBackgroundTask(expirationHandler: nil)
        DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
            self.peripheralManager.startAdvertising([CBAdvertisementDataLocalNameKey: "YourDeviceName", CBAdvertisementDataServiceUUIDsKey: [self.uuid]])
            UIApplication.shared.endBackgroundTask(backgroundTask)
        }
    }
    
    func applicationWillEnterForeground(_ application: UIApplication) {
        peripheralManager.stopAdvertising()
    }
    
}

I tried using "CLLocationManager" allowsBackgroundLocationUpdates and in sign-in & capabilities added

  • background mode and enables
  • location updates
  • uses Bluetooth LE accessory
  • background fetch
  • background processing
  • Acts as a Bluetooth LE accessory

These are the methods I tried that work perfectly in the foreground. Are there any other methods to work the beacon app to work in background mode?

Rohit Gupta
  • 4,022
  • 20
  • 31
  • 41

1 Answers1

0

Unfortunately iOS does not allow apps to advertise iBeacon in the background. This is an intentional operating system restriction.

The only alternative for background advertising under user control is to use not iBeacon but OverflowArea advertisements. I wrote a blog post on how to do this advertising in the background.

davidgyoung
  • 63,876
  • 14
  • 121
  • 204