Once we've created a central manager and the iOS device's Bluetooth is powered on, the CBCentralManager will automatically call the centralManagerDidUpdateState function on its delegate (which is the BLECentralViewController in our app)

Download: file
func centralManagerDidUpdateState(_ central: CBCentralManager) {
        if central.state == CBManagerState.poweredOn {
            // We will just handle it the easy way here: if Bluetooth is on, proceed...start scan!
            print("Bluetooth Enabled")
            startScan()
       
        } else {
            //If Bluetooth is off, display a UI alert message saying "Bluetooth is not enable" and "Make sure that your bluetooth is turned on"
            print("Bluetooth Disabled- Make sure your Bluetooth is turned on")
            
            let alertVC = UIAlertController(title: "Bluetooth is not enabled", message: "Make sure that your bluetooth is turned on", preferredStyle: UIAlertControllerStyle.alert)
            let action = UIAlertAction(title: "ok", style: UIAlertActionStyle.default, handler: { (action: UIAlertAction) -> Void in
                self.dismiss(animated: true, completion: nil)
            })
            alertVC.addAction(action)
            self.present(alertVC, animated: true, completion: nil)
        }
    }

The centralManagerDidUpdateState function lets the device know when bluetooth is enabled or disabled. We can use this function to respond to bluetooth being powered on or off.  In our example code, we do this by comparing central.state with the desired value of CBManagerState.poweredOn.  

If these states match, we call the startScan function to begin looking for BLE peripherals nearby.  If they don't match, the app presents an alert view notifying the user that they need to turn Bluetooth on.

Scanning for Peripherals

Once the CBCentralManager is up and powered on, we call the startScan() function to look for peripherals are around us.

Download: file
func startScan() {
        print("Now Scanning...")
        self.timer.invalidate()
        centralManager?.scanForPeripherals(withServices: [BLEService_UUID] , options: [CBCentralManagerScanOptionAllowDuplicatesKey:false])
        Timer.scheduledTimer(timeInterval: 17, target: self, selector: #selector(self.cancelScan), userInfo: nil, repeats: false)
  }

The startScan() function calls scanForPeripherals(withServices) and creates a timer which stops scanning after 17 seconds by calling self.cancelScan().

This Timer object is not required but allows for a reasonable amount of time to discover all nearby peripherals.

Using CBUUIDs

Download: file
centralManager?.scanForPeripherals(withServices: [BLEService_UUID] , options: nil)

Notice that when we called scanForPeripherals(withServices), we included [BLEService_UUID] to specify what services we're looking for.  The BLEService_UUID variable (defined in the UUIDKey.swift file) is a CBUUID object.  CBUUIDs are unique identifiers used throughout CoreBluetooth and are essential to BLE communication.  Each peripheral, service, and characteristic has its own CBUUID.

By specifying what service we're looking for, we've told the CBCentralManager to ignore all peripherals which don't advertise that specific service, and return a list of only the peripherals which do offer that service.

Discovering Peripherals

Now that we've started scanning, what happens when we discover a peripheral?

Every time a peripheral is discovered, the CBCentralManager will notify us by calling the centralManager(_:didDiscover:advertisementData:rssi:) function on its delegate.

This function provides the following information about the newly discovered peripheral:

  • The CBCentralManager providing the update.
  • The discovered peripheral as a CBPeripheral object
  • A Dictionary containing the peripheral's advertisement data.
  • The current received signal strength indicator (RSSI) of the peripheral, in decibels.
Download: file
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral,advertisementData: [String : Any], rssi RSSI: NSNumber) {
        stopScan()
  	self.peripherals.append(peripheral)
        self.RSSIs.append(RSSI)
        peripheral.delegate = self
        peripheral.discoverServices([BLEService_UUID])
        self.baseTableView.reloadData()
        if blePeripheral == nil {
            print("We found a new pheripheral devices with services")
            print("Peripheral name: \(peripheral.name)")
            print("**********************************")
            print ("Advertisement Data : \(advertisementData)")
            blePeripheral = peripheral
        }
   }

In our implementation of this function we perform the following actions:

  1. Stop scanning for peripherals (for our example app, we're only interested in the first one which appears)
  2. Add the newly discovered peripheral to an array of peripherals.
  3. Add the new peripheral's RSSI to an array of RSSIs.
  4. Set the peripheral's delegate to self (BLECentralViewController)
  5. Tell the Central Manager to discover more details about the peripheral's service
  6. Reload the table view which uses our peripherals array as a data source
  7. Set the blePeripheral variable to the new peripheral and print relevant information in the debug window

Next up - we'll actually connect to that peripheral.

This guide was first published on Jul 27, 2017. It was last updated on Jul 27, 2017.

This page (Scanning for Peripherals) was last updated on Nov 24, 2020.