How to Use the centralManager didDiscover Delegate in iOS Core Bluetooth

Developer debugging code across multiple monitors at a modern workspace, focused on resolving software issues.

Your scan is running and the didDiscover delegate is firing. You are looking at four parameters you probably have not worked with before, and what you pull from each one (and when) is what separates a scanning implementation that works from one that only mostly works.

The centralManager(_:didDiscover:peripheral:advertisementData:rssi:) delegate fires every time your iOS app detects a BLE advertisement packet in range. It hands you four parameters. Central, peripheral, advertisementData, and RSSI each carry different information you need to identify, evaluate, and connect to the right device. Here is what each one gives you and how to use it.

If you still need scanning configured, start with the iOS Core Bluetooth Ultimate Guide and come back once the delegate is firing.


The Method Signature

Each call to centralManager(_:didDiscover:peripheral:advertisementData:rssi:) reflects a detected advertisement packet of a BLE peripheral in range. The number of calls per device in a given scanning session depends on the scanning options you provided, as well as the range and advertising status of the peripheral itself. For more on those options, see the scanning section of the Ultimate Guide.

The method signature looks like this:

optional func centralManager(_ central: CBCentralManager,
                              didDiscover peripheral: CBPeripheral,
                              advertisementData: [String: Any],
                              rssi RSSI: NSNumber)

The Parameters

central: CBCentralManager

The central manager object that discovered the device while scanning.

peripheral: CBPeripheral

A CBPeripheral object representing the BLE peripheral that was discovered. It carries the device name if the peripheral broadcasts one, and an iOS-assigned UUID. It does not carry the advertisement data or RSSI — those come through the other two parameters.

advertisementData: [String: Any]

A dictionary representation of the data included in the detected advertisement packet. Core Bluetooth parses and organizes this data with a set of built-in keys, which we cover in the next section.

rssi: NSNumber

The relative signal quality in decibels of the peripheral at the time of the received advertisement packet. Because RSSI is a relative measure, the interpreted value by a central can vary by chipset. As returned by most iOS devices through Core Bluetooth, it generally ranges from -30 to -99, with -30 being the strongest.


Reading advertisementData

The advertisementData dictionary is the most useful thing the callback hands you before a connection is established. Core Bluetooth does the work of parsing the raw advertisement packet and organizing it around a set of named keys. Here is what each key carries:

Advertisement KeyValue TypeDescription
CBAdvertisementDataManufacturerDataKeyNSDataCustom data provided by peripheral manufacturers. Can be used by peripherals for many things, like storing a device serial number or other identifying information.
CBAdvertisementDataServiceDataKey[CBUUID: NSData]Dictionary with CBUUID keys representing services, and custom data associated with those services. This is usually the best place for peripherals to store custom identifying data for pre-connection use.
CBAdvertisementDataServiceUUIDsKey[CBUUID]An array of service UUIDs, usually reflecting one or more of the services contained in the device’s GATT table.
CBAdvertisementDataOverflowServiceUUIDsKey[CBUUID]An array of service UUIDs from the overflow area of the advertisement data. For advertised services that did not fit in the main advertising packet. If a service UUID you expect to see in CBAdvertisementDataServiceUUIDsKey is missing, check here before assuming it wasn’t advertised.
CBAdvertisementDataTxPowerLevelKeyNSNumberThe transmitting power level of the peripheral, if provided in the advertising packet.
CBAdvertisementDataIsConnectableNSNumberA Boolean value in NSNumber form (0 or 1) that is 1 if the peripheral is currently connectable.
CBAdvertisementDataSolicitedServiceUUIDsKey[CBUUID]An array of solicited service UUIDs. See the scanning options section of the Ultimate Guide for context on solicited services.

Reading and Interpreting RSSI Values

RSSI is a relative measure, and a single reading is noisier than it looks. Signal strength fluctuates with environment, obstructions, device orientation, and chipset behavior, so one value at one moment is not a reliable basis for decisions. If you need to make choices based on signal strength — filtering distant devices, ranking candidates — sampling across multiple readings and averaging them will give you something more useful than any individual value.

Getting those repeated readings requires CBCentralManagerScanOptionAllowDuplicatesKey set to true. The Ultimate Guide to Apple Core Bluetooth covers that option and the power tradeoff it introduces if you would like to dive deeper.


Storing Results

Once the scan callback returns a CBPeripheral object, you must retain a strong reference to it in your code. The central manager does not internally retain strong references to its discovered peripherals. If you call connect immediately from didDiscover and let that function block complete without storing the peripheral, the object will be deallocated and any connection or pending connection will break.

A common approach is a struct with optional properties for each value the callback returns:

// In main class
var discoveredPeripherals = [CBPeripheral]()

func startScan() {
    centralManager.scanForPeripherals(withServices: nil, options: nil)
}

// In CBCentralManagerDelegate class/extension
func centralManager(_ central: CBCentralManager,
                    didDiscover peripheral: CBPeripheral,
                    advertisementData: [String: Any],
                    rssi RSSI: NSNumber) {
    self.discoveredPeripherals.append(peripheral)
}

Storing only the CBPeripheral also loses the RSSI and advertisement data returned in the same delegate call. CBPeripheral has no internal storage for either. If you want access to them later, create a wrapper class or struct for the CBPeripheral object that includes storage for those items.


Where to Go Next

The advertisement packet has given you what you need to identify the right peripheral. The next step is connecting to it. Check out the section of our iOS Core Bluetooth Ultimate Guide on connecting and disconnecting for the full connection flow, from connect(_:options:) through didConnect and didFailToConnect. Once you have a connected CBPeripheral in hand, How to Set Up iOS Core Bluetooth for Discovering Services and Characteristics covers everything that comes next.

Share:

Punch Through
Punch Through
We’re a team of engineers who obsess over making connected things actually work — reliably, securely, and without the handwaving. From BLE to backend, we build the software and systems behind connected medical devices and custom connected products that can’t afford to fail.

Subscribe to stay up-to-date with our latest articles and resources.