Skip to content

3. Scanning peripherals

paweljaneczek edited this page Jul 2, 2018 · 3 revisions

To start any interaction with bluetooth devices, you have to scan some of them first. So - get ready!

Basic

manager.scanForPeripherals(withServices: serviceIds)
    .subscribe(onNext: { scannedPeripheral in
        let advertisementData = scannedPeripheral.advertisementData
    })

This is the simplest version of this operation. After subscribing to the observable, the scan is performed infinitely. What you receive from method is ScannedPeripheral instance, that provides access to following information:

  • Peripheral: an object that you can use to perform actions like connecting, discovering services etc.
  • AdvertisementData: a strongly-typed wrapper around CBPeripheral advertisement data dictionary. Thanks to it, you no longer have to worry about all of the keys needed to pull out information.
  • RSSI

Cancelling

Scanning operation is not cancelled by default. It's the user's responsibility to perform that when scanning in not needed anymore. The easiest solutions is just to dispose scanning:

let disposable = centralManager.scanForPeripherals(withServices: nil)
    .subscribe(onNext: { print("Peripheral scaned \($0)") })
// after some time
disposable.dispose()

Here come other possibilities, thanks to awesome RxSwift operators:

manager.scanForPeripherals(withServices: [serviceIds]).take(1)
//Doing this, after first received result, scan is immediately cancelled.

Ok, that's fun, but what if you also want to apply timeout policy? That's also easy to do:

manager.scanForPeripherals(withServices: [serviceIds]).timeout(3.0, timerScheduler)

As you can see: thanks to all available RxSwift operators, you might create really interesting and complex usage scenarios in a simple way like for example retrying scans, if you receive timeout.

Waiting for proper BluetoothState

As describe in Manager State, in order to perform work with bluetooth, your manager should be in .poweredOn state.

Let's see how it looks with scanning:

manager.observeState()
	.startWith(manager.state)
	.filter { $0 == .poweredOn }
	.timeout(3.0, scheduler)
	.take(1)
	.flatMap { _ in manager.scanForPeripherals(withServices: [serviceId]) }

Firstly, use CentralManager.state as a start value, next filter .poweredOn from states stream. Like above, we want to apply timeout policy to state changes. Also, we use take to be sure, that after getting .poweredOn state, nothing else ever will be emitted by the observable.

In the last flatMap operation, the bluetooth is ready to perform further operations.

Note: Be aware that you can only have one scanning at a time. If you do scanForPeripherals while different scanning is in progress , you will receive .scanInProgress error.