Creating a Simple BLE Scanner on iPhone


Here are steps to create a simple BLE (Bluetooth Low Energy) scanner app for iPhone (for my own learning purpose). The goal is to enable below features.

  • Scan for all advertising devices around.
  • Print the device name, RSSI (signal strength), and advertising data [1] for each discovered device in the debug console output [2].

* It doesn’t filter any specific UUID, doesn’t connect to any peripheral device, and doesn’t show anything on app screen.

 

 

Prerequisites (parentheses indicate my environment)

  • Xcode (10.0) ruining on Mac (10.14)
  • iPhone (iPhone 8 with iOS 12.0.1)
    * An actual iOS device is required since Bluetooth is not supported in Xcode simulator.

 

Steps
1. Launch Xcode and create a Single View App.

2. Open ViewController.swift file and import CoreBluetooth framework.

import CoreBluetooth

3. In order to scan, iPhone need to be BLE central. So, add CBCentralManagerDelegate protocol to ViewController class.

class ViewController: UIViewController, CBCentralManagerDelegate {

4. Then, Xcode will prompt an error. Click on “Fix” button and it will create  centralManagerDidUpdateState method which will be called when the central manager’s state is changed. At startup, this method is called after instantiating CBCentralManager.

5. Declare a variable for CBCentralManager in ViewController class.

private var centralManager : CBCentralManager!

6. Instantiate CBCentralManager in viewDidLoad method.

centralManager = CBCentralManager(delegate: self, queue: nil, options: nil)

7. In centralManagerDidUpdateState method, start scanning when the state is poweredOn.

if central.state == .poweredOn {
    print("Bluetooth is On")
    centralManager.scanForPeripherals(withServices: nil, options: nil)
} else {
    print("Bluetooth is not active")
}

8. Define centralManager didDiscover method in ViewController class. It will be called when any advertising device is discovered. Inside the method, it prints the device name, RSSI, and advertising data.

public func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
    print("\nName   : \(peripheral.name ?? "(No name)")")
    print("RSSI   : \(RSSI)")
    for ad in advertisementData {
        print("AD Data: \(ad)")
    }
}

 

 

Test
1. Connect iPhone to Mac.

2. Make sure Bluetooth setting is ON on the connected iPhone.

3. Build and run the program, and check the console output on Xcode. Below is an example of the output.

Bluetooth is On

Name   : Flex
RSSI   : -55
AD Data: (key: "kCBAdvDataIsConnectable", value: 1)
AD Data: (key: "kCBAdvDataLocalName", value: Flex)
AD Data: (key: "kCBAdvDataServiceUUIDs", value: <__NSArrayM 0x282a0c600>(
ADAB3BF6-6E7D-4601-BDA2-BFFAA68956BA
)
)
AD Data: (key: "kCBAdvDataTxPowerLevel", value: -6)
AD Data: (key: "kCBAdvDataServiceData", value: {
    "Device Information" = <0704>;
})

Name   : Apple TV
RSSI   : -89
AD Data: (key: "kCBAdvDataIsConnectable", value: 1)

Name   : (No name)
RSSI   : -71
AD Data: (key: "kCBAdvDataManufacturerData", value: <06000109 210ab8f3 61ae6190 4445534b 544f502d 4e424c54 533655>)
AD Data: (key: "kCBAdvDataIsConnectable", value: 0)

Name   : (No name)
RSSI   : -96
AD Data: (key: "kCBAdvDataIsConnectable", value: 1)

In the example above, it detected my Fitbit Flex (Line 3-14) and some other devices. All possible advertising data type on CoreBluetooth are listed here.

 

References
[1] Advertising Data Type – Bluetooth SIG
[2] Console output in Xcode – Apple Developer

 

 

Sponsor Link

5 Comments

  1. This was super useful and worked a treat for me. Thank you very much – very clear instructions

  2. Hi, i am getting (no names) in the name field. I followed your steps thoroughly. Where did I go wrong?
    Please advice.

  3. Hi i have followed your tutorial im getting no name and I am confused why I am getting this

Comments are closed.