Core Bluetooth enables your app to communicate with Bluetooth Low Energy devices. Learn to scan, connect, and exchange data.
Core Bluetooth Setup
Enable Bluetooth
- 1Go to Project Settings > Signing & Capabilities
- 2Add "Bluetooth" capability
- 3Add Bluetooth-Always-If-You-Want-It-Usage key to Info.plist
Initialize Central Manager
swift
import CoreBluetooth
import SwiftUI
class BluetoothManager: NSObject, ObservableObject, CBCentralManagerDelegate {
private var centralManager: CBCentralManager!
private var peripherals: [CBPeripheral] = []
@Published var discoveredPeripherals: [CBPeripheral] = []
@Published var connectedPeripheral: CBPeripheral?
@Published var isScanning = false
override init() {
super.init()
centralManager = CBCentralManager(delegate: self, queue: nil)
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .poweredOn:
print("Bluetooth is powered on")
case .poweredOff:
print("Bluetooth is powered off")
case .unauthorized:
print("Bluetooth is unauthorized")
default:
print("Bluetooth state: \(central.state.rawValue)")
}
}
}Scanning
Scan for Peripherals
swift
extension BluetoothManager {
func startScanning() {
guard centralManager.state == .poweredOn else {
print("Bluetooth not powered on")
return
}
isScanning = true
centralManager.scanForPeripherals(
withServices: nil,
options: [CBCentralManagerScanOptionAllowDuplicatesKey: false]
)
}
func stopScanning() {
centralManager.stopScan()
isScanning = false
}
func scanForSpecificServices() {
let serviceUUIDs = [
CBUUID(string: "180D"), // Heart Rate Service
CBUUID(string: "180A") // Battery Service
]
centralManager.scanForPeripherals(withServices: serviceUUIDs, options: nil)
}
}Handle Discovery
swift
extension BluetoothManager {
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String: Any], rssi RSSI: NSNumber) {
if !discoveredPeripherals.contains(where: { $0.identifier == peripheral.identifier }) {
discoveredPeripherals.append(peripheral)
print("Discovered: \(peripheral.name ?? "Unknown")")
}
}
}Connecting
Connect to Peripheral
swift
extension BluetoothManager {
func connect(to peripheral: CBPeripheral) {
peripheral.delegate = self
centralManager.connect(peripheral, options: nil)
}
func disconnect(peripheral: CBPeripheral) {
centralManager.cancelPeripheralConnection(peripheral)
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
connectedPeripheral = peripheral
peripheral.discoverServices(nil)
print("Connected to \(peripheral.name ?? "Unknown")")
}
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
if let error = error {
print("Disconnected with error: \(error)")
} else {
print("Disconnected")
}
if connectedPeripheral?.identifier == peripheral.identifier {
connectedPeripheral = nil
}
}
}Services and Characteristics
Discover Services
swift
extension BluetoothManager {
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
guard let services = peripheral.services else { return }
for service in services {
print("Service: \(service.uuid)")
if let characteristics = service.characteristics {
print("Characteristics: \(characteristics.count)")
}
peripheral.discoverCharacteristics(nil, for: service)
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
guard let characteristics = service.characteristics else { return }
for characteristic in characteristics {
print("Characteristic: \(characteristic.uuid)")
if characteristic.properties.contains(.read) {
peripheral.readValue(for: characteristic)
}
if characteristic.properties.contains(.notify) {
peripheral.setNotifyValue(true, for: characteristic)
}
}
}
}Data Transfer
Read and Write Data
swift
extension BluetoothManager {
func readValue(from characteristic: CBCharacteristic, peripheral: CBPeripheral) {
peripheral.readValue(for: characteristic)
}
func writeValue(_ data: Data, to characteristic: CBCharacteristic, peripheral: CBPeripheral) {
peripheral.writeValue(data, for: characteristic, type: .withResponse)
}
func writeValueWithoutResponse(_ data: Data, to characteristic: CBCharacteristic, peripheral: CBPeripheral) {
guard characteristic.properties.contains(.writeWithoutResponse) else {
print("Characteristic doesn't support write without response")
return
}
peripheral.writeValue(data, for: characteristic, type: .withoutResponse)
}
}Handle Data Updates
swift
extension BluetoothManager {
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
guard let data = characteristic.value else { return }
// Process data
processIncomingData(data: data, from: characteristic)
print("Received data: \(data as NSData)")
}
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
if let error = error {
print("Write error: \(error)")
} else {
print("Write successful")
}
}
private func processIncomingData(data: Data, from characteristic: CBCharacteristic) {
// Parse data based on characteristic UUID
if characteristic.uuid.uuidString == "2A37" {
// Heart rate measurement
parseHeartRate(data: data)
} else if characteristic.uuid.uuidString == "2A19" {
// Battery level
parseBatteryLevel(data: data)
}
}
private func parseHeartRate(data: Data) {
let heartRate = data.withUnsafeBytes { pointer in
pointer.load(as: UInt8.self)
}
DispatchQueue.main.async {
// Update UI with heart rate
print("Heart Rate: \(heartRate) BPM")
}
}
private func parseBatteryLevel(data: Data) {
let batteryLevel = data.withUnsafeBytes { pointer in
pointer.load(as: UInt8.self)
}
DispatchQueue.main.async {
// Update UI with battery level
print("Battery: \(batteryLevel)%")
}
}
}Peripheral Mode
Create Peripheral
swift
import CoreBluetooth
class PeripheralManager: NSObject, CBPeripheralManagerDelegate {
private var peripheralManager: CBPeripheralManager?
private var transferCharacteristic: CBMutableCharacteristic?
func startAdvertising() {
peripheralManager = CBPeripheralManager(delegate: self, queue: nil)
let serviceUUID = CBUUID(string: "180D")
let characteristicUUID = CBUUID(string: "2A37")
let service = CBMutableService(type: serviceUUID)
transferCharacteristic = CBMutableCharacteristic(
type: characteristicUUID,
properties: [.read, .write, .notify],
value: nil,
permissions: [.readable, .writeable]
)
service.characteristics = [transferCharacteristic!]
let advertisementData: [String: Any] = [
CBAdvertisementDataServiceUUIDsKey: [serviceUUID],
CBAdvertisementDataLocalNameKey: "My Device"
]
peripheralManager?.startAdvertising(advertisementData)
}
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
if peripheral.state == .poweredOn {
let service = CBMutableService(type: CBUUID(string: "180D"))
peripheral.add(service)
}
}
func stopAdvertising() {
peripheralManager?.stopAdvertising()
}
}Best Practices
- 1Permissions: Request Bluetooth permissions
- 2Scanning: Scan efficiently
- 3Connection: Manage connections properly
- 4Data: Handle data transfer carefully
- 5Battery: Optimize for battery life
- 6Error Handling: Handle all errors
- 7Testing: Test on real devices
- 8Background: Support background modes
Core Bluetooth enables powerful BLE device integration!