My goal is to advertise a beacon in background mode.
The expected result is: The beacon continue to advertise in background mode.
The actual result is: The beacon stop advertising in background mode.
What I've tried:
- Background task can extend time for 30 seconds but this is not a solution.
 
import Foundation
import CoreLocation
import CoreBluetooth
protocol BeaconBroadcaster {
    
    /// Broadcast to be find by scanner.
    /// - Parameters:
    ///   - uuid: used by broadcast scanner to find specific uuid
    ///   - major and minor: used by broadcast scanner to differentiate beacon with identical uuid
    func startBroadcasting(uuid: UUID, major: UInt16, minor: UInt16) -> BeaconBroadcasterStartBroadcastingEvent
    
    /// stop broadcaster
    func stopBroadcasting() -> BeaconBroadcasterStopBroadcastingEvent
}
class BeaconBroadcasterImpl: BeaconBroadcaster {
    private var peripheralManager = CBPeripheralManager()
    
    fileprivate init() {
#if DEBUG
        print("\(type(of: self)) \(#function)")
#endif
    }
    
    func startBroadcasting(uuid: UUID, major: UInt16, minor: UInt16) -> BeaconBroadcasterStartBroadcastingEvent {
        if peripheralManager.state == .poweredOff {
            return .BLUETOOTH_NOT_ENABLED
        }
        
        if peripheralManager.state == .unauthorized {
            return .BLUETOOTH_NOT_AUTHORIZED
        }
        
        if peripheralManager.state != .poweredOn {
            return .BLUETOOTH_NOT_AUTHORIZED
        }
        
        if peripheralManager.isAdvertising {
            _ = stopBroadcasting()
        }
        
        let bundleURL = Bundle.main.bundleIdentifier!
        let constraint = CLBeaconIdentityConstraint(uuid: uuid, major: major, minor: minor)
        let region = CLBeaconRegion(beaconIdentityConstraint: constraint, identifier: bundleURL)
        let peripheralData = region.peripheralData(withMeasuredPower: nil) as? [String: Any]
        peripheralManager.startAdvertising(peripheralData)
        
        return .BROADCASTER_IS_STARTING
    }
    
    func stopBroadcasting() -> BeaconBroadcasterStopBroadcastingEvent {
        if !peripheralManager.isAdvertising {
            return .BROADCASTER_HAS_ALREADY_STOPPED
        }
        
        peripheralManager.stopAdvertising()
        
        return .BROADCASTER_IS_STOPPING
    }
}
extension BeaconBroadcasterImpl {
    
    /// Usage: use force unwrap, it will always return non-nil value
    static var shared: BeaconBroadcaster? {
        get {
            if sharedClosure == nil {
                sharedClosure = BeaconBroadcasterImpl()
            }
            
            return sharedClosure
        }
        set {
            sharedClosure = newValue
        }
    }
    
    private static var sharedClosure: BeaconBroadcaster?
}