Mark Tyers
Mark Tyers

Reputation: 3239

CoreBluetooth Advertising not starting

I am trying to write a simple app that constantly broadcasts a 'beacon' even when the app is not active. I know that using CoreLocation will switch this off when the app is not in use so I was trying to build a solution using Core Bluetooth. The trouble is that I can't get the app to start advertising.

import UIKit
import CoreBluetooth

class ViewController: UIViewController, CBCentralManagerDelegate, CBPeripheralManagerDelegate {

    var centralManager:CBCentralManager = CBCentralManager()
    var peripheralManager:CBPeripheralManager = CBPeripheralManager()
    let uuid:CBUUID = CBUUID(string: "DCEF54A2-31EB-467F-AF8E-350FB641C97B")

    override func viewDidLoad() {
        super.viewDidLoad()
        self.peripheralManager = CBPeripheralManager(delegate: self, queue: nil)
        self.centralManager.delegate = self
        let advertisingData = [CBAdvertisementDataLocalNameKey:"my-peripheral", CBAdvertisementDataServiceUUIDsKey: uuid]
        peripheralManager.startAdvertising(advertisingData)
        centralManager.scanForPeripheralsWithServices([uuid], options: nil)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func peripheralManagerDidStartAdvertising(peripheral: CBPeripheralManager, error: NSError?) {
        print("started advertising")
        print(peripheral)
    }

    func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber) {
        print("peripheral discovered")
        print("peripheral: \(peripheral)")
        print("RSSI: \(RSSI)")
    }

    func centralManagerDidUpdateState(central: CBCentralManager) {
        print("central state updated")
        print(central.description)
        if central.state == .PoweredOff {
            print("bluetooth is off")
        }
        if central.state == .PoweredOn {
            print("bluetooth is on")
            let advertisingData = [CBAdvertisementDataLocalNameKey:"my-peripheral", CBAdvertisementDataServiceUUIDsKey: uuid]
            let service = CBMutableService(type: uuid, primary: true)
            self.peripheralManager.addService(service)
            peripheralManager.startAdvertising(advertisingData)
            centralManager.scanForPeripheralsWithServices(nil, options: nil)
        }
        if central.state == .Unsupported {
            print("bluetooth is unsupported on this device")
        }
    }

    func peripheralManagerDidUpdateState(peripheral: CBPeripheralManager) {
        print("peripheral state updated")
        print("\(peripheral.description)")
    }

}

I have installed this on two devices, the issue seems to be in the transmission of the advert since peripheralManagerDidStartAdvertising() is never called.

Upvotes: 1

Views: 3322

Answers (3)

Alexander Volkov
Alexander Volkov

Reputation: 8372

The issue is related to immediate call .startAdvertising() right after .addService() call.

You must call .startAdvertising() ONLY after func peripheralManager(_ peripheral: CBPeripheralManager, didAdd service: CBService, error: Error?) delegate method is called.

Upvotes: 2

cristallo
cristallo

Reputation: 2089

The CBAdvertisementDataServiceUUIDsKey value passed to startAdvertising should be an array of CBUUID objects but you are passing a single CBUUID

let advertisingData = [CBAdvertisementDataLocalNameKey:"my-peripheral", CBAdvertisementDataServiceUUIDsKey: [uuid]]

Upvotes: 0

Gyuri T
Gyuri T

Reputation: 290

If you use this init method the centralManagerDidUpdateState is going to work. You can set the CBCentralManagerScanOptionAllowDuplicatesKey option if you would like to see the beacons continually.

import UIKit
import CoreBluetooth

class ViewController: UIViewController, CBCentralManagerDelegate, CBPeripheralManagerDelegate {

var centralManager:CBCentralManager!
var peripheralManager:CBPeripheralManager = CBPeripheralManager()
let uuid:CBUUID = CBUUID(string: "DCEF54A2-31EB-467F-AF8E-350FB641C97B")

override func viewDidLoad() {
    super.viewDidLoad()
    self.peripheralManager = CBPeripheralManager(delegate: self, queue: nil)
    self.centralManager = CBCentralManager(delegate: self, queue: nil)
    let advertisingData = [CBAdvertisementDataLocalNameKey:"my-peripheral", CBAdvertisementDataServiceUUIDsKey: uuid]
    peripheralManager.startAdvertising(advertisingData)
    centralManager.scanForPeripheralsWithServices([uuid], options: [ CBCentralManagerScanOptionAllowDuplicatesKey : true])
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

func peripheralManagerDidStartAdvertising(peripheral: CBPeripheralManager, error: NSError?) {
    print("started advertising")
    print(peripheral)
}


func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber) {
    print("peripheral discovered")
    print("peripheral: \(peripheral)")
    print("advertisement: \(advertisementData)")
    if let data = advertisementData["kCBAdvDataServiceData"] {
        print("found advert data: \(data)")
    }
    print("RSSI: \(RSSI)")
}

func startAdvert(){
    let advertisingData = [CBAdvertisementDataLocalNameKey:"my-peripheral", CBAdvertisementDataServiceUUIDsKey: uuid]
    peripheralManager.startAdvertising(advertisingData)
}

func centralManager(central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: NSError?) {
    print("peripheral disconnected")
    print("peripheral: \(peripheral)")
}

func centralManagerDidUpdateState(central: CBCentralManager) {
    print("central state updated")
    print(central.description)
    if central.state == .PoweredOff {
        print("bluetooth is off")
    }
    if central.state == .PoweredOn {
        print("bluetooth is on")
        centralManager.scanForPeripheralsWithServices(nil, options: [ CBCentralManagerScanOptionAllowDuplicatesKey : true])
        startAdvert()
    }
    if central.state == .Unsupported {
        print("bluetooth is unsupported on this device")
    }
}

func peripheralManagerDidUpdateState(peripheral: CBPeripheralManager) {
    print("peripheral state updated")
    print("\(peripheral.description)")
}

}

Upvotes: 0

Related Questions