Core Bluetooth — framework Apple do komunikacji z Bluetooth Low Energy na iOS, iPadOS i macOS. Framework udostępnia pełny zestaw API do pracy w obu rolach BLE: urządzenie centralne (CBCentralManager) do skanowania i podłączania się do peryferiów oraz urządzenie peryferyjne (CBPeripheralManager) do emulacji serwera BLE. Core Bluetooth abstrahuje stos protokołu BLE od fizycznego radia do aplikacyjnego profilu GATT. Według Apple Developer, 2026, Core Bluetooth to jedyne oficjalne API Apple do BLE-rozwoju, obsługujące BLE 4.0–5.4 z extended advertising, 2M PHY i LE Audio.
Najważniejsze
Core Bluetooth dzieli stos BLE na dwie logiczne role określone przez specyfikację Bluetooth SIG. Rola urządzenia centralnego (Central) jest reprezentowana przez klasę CBCentralManager — inicjuje skanowanie, ustanawia połączenia i zarządza listą podłączonych CBPeripheral. Rola urządzenia peryferyjnego (Peripheral) jest reprezentowana przez CBPeripheralManager — publikuje usługi i charakterystyki, odpowiada na żądania centrali i wysyła powiadomienia. Jedna sesja iOS może jednocześnie pracować w obu rolach na różnych radiach BLE, ale typowa aplikacja używa jednej roli.
Architektura Core Bluetooth obejmuje pięć kluczowych abstrakcji. CBCentralManager zarządza stanem adaptera Bluetooth urządzenia: poweredOn (gotowy do pracy), poweredOff (Bluetooth wyłączony), unauthorized (brak uprawnień), unsupported (BLE niedostępny). CBPeripheral reprezentuje zdalne urządzenie BLE z jego UUID, nazwą, RSSI i hierarchią GATT. CBService — logiczna grupa charakterystyk. CBCharacteristic — punkt danych do odczytu/zapisu/powiadomień. CBPeripheralManager tworzy lokalny serwer GATT do emulacji peryferii.
| Klasa | Rola | Główne metody |
|---|---|---|
| CBCentralManager | Urządzenie centralne | scanForPeripherals, connect, cancelPeripheralConnection, retrievePeripherals |
| CBPeripheral | Zdalna peryferia | discoverServices, discoverCharacteristics, readValue, writeValue, setNotifyValue |
| CBPeripheralManager | Lokalna peryferia | addService, removeService, startAdvertising, respondToRequest, updateValue |
| CBCentral | Zdalna centrala | maximumUpdateValueLength, identifier, ancsAuthorized |
Stany CBCentralManager zarządzają wszystkimi operacjami BLE. Przy starcie aplikacji centralManagerDidUpdateState jest wywoływany z bieżącym stanem Bluetooth. Jeśli stan nie jest .poweredOn, wszelkie wywołania BLE są ignorowane przez system. Deweloper musi sprawdzać state przed każdym skanowaniem i podłączeniem. Przejście z .poweredOff do .poweredOn następuje po włączeniu Bluetooth w Ustawieniach iOS — delegat otrzymuje ponowne wywołanie, a aplikacja może wznowić skanowanie.
CBCentralManager — punkt wejścia dla wszystkich operacji BLE po stronie urządzenia centralnego. Inicjalizacja przyjmuje delegata (CBCentralManagerDelegate) i kolejkę DispatchQueue — zalecenie Apple to używanie main queue dla prostoty lub serial queue dla wydajności. Po inicjalizacji framework automatycznie sprawdza stan Bluetooth i wywołuje centralManagerDidUpdateState: — pierwszy obowiązkowy delegat do obsługi.
Skanowanie uruchamiane jest metodą scanForPeripheralsWithServices:options:. Pierwszy parametr to tablica CBUUID usług do filtrowania: jeśli znane są UUID interesujących usług, ich przekazanie zmniejsza zużycie energii i czas wyszukiwania. Jeśli nil, wykrywane są wszystkie urządzenia BLE w zasięgu. Opcje obejmują .allowDuplicatesKey (ponowne wykrywanie tego samego urządzenia) i .solicitedServiceUUIDsKey (dla usług publikowanych na centrali).
import CoreBluetooth
class BLECentral: NSObject {
private var centralManager: CBCentralManager!
private var discoveredPeripherals: [CBPeripheral] = []
override init() {
super.init()
centralManager = CBCentralManager(delegate: self, queue: .main)
}
// Rozpocznij skanowanie BLE
func startScan() {
guard centralManager.state == .poweredOn else {
print("Bluetooth niedostępny")
return
}
// Skanuj wszystkie urządzenia (nil = brak filtra)
centralManager.scanForPeripherals(withServices: nil,
options: [CBCentralManagerScanOptionAllowDuplicatesKey: true])
}
// Zatrzymaj skanowanie
func stopScan() {
centralManager.stopScan()
}
// Podłącz do wybranego urządzenia
func connect(to peripheral: CBPeripheral) {
centralManager.connect(peripheral, options: nil)
}
}
// MARK: - CBCentralManagerDelegate
extension BLECentral: CBCentralManagerDelegate {
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOn {
startScan()
}
}
func centralManager(_ central: CBCentralManager,
didDiscover peripheral: CBPeripheral,
advertisementData: [String : Any],
rssi: NSNumber) {
if !discoveredPeripherals.contains(where: { $0.identifier == peripheral.identifier }) {
discoveredPeripherals.append(peripheral)
print("Found devices: \(peripheral.name ?? "Unknown"), RSSI: \(rssi)")
}
}
func centralManager(_ central: CBCentralManager,
didConnect peripheral: CBPeripheral) {
print("Connected: \(peripheral.identifier)")
peripheral.delegate = self
peripheral.discoverServices(nil)
}
func centralManager(_ central: CBCentralManager,
didDisconnectPeripheral peripheral: CBPeripheral,
error: Error?) {
print("Disconnected: \(peripheral.identifier)")
}
}
Klasa BLECentral demonstruje pełny cykl skanowania i podłączania urządzeń BLE. centralManagerDidUpdateState uruchamia skanowanie przy włączonym Bluetooth. didDiscoverPeripheral zbiera znalezione urządzenia w tablicy discoveredPeripherals z deduplikacją po identifier. Po podłączeniu (didConnect) natychmiast uruchamiane jest wykrywanie usług — to obowiązkowy krok przed jakimikolwiek operacjami GATT.
CBPeripheralManager — klasa do emulacji urządzenia peryferyjnego BLE na iOS. Aplikacja w roli peryferii może publikować swoje usługi i charakterystyki, przyjmować przychodzące żądania odczytu/zapisu od urządzenia centralnego i wysyłać powiadomienia. CBPeripheralManager jest używany do akcesoriów BLE emulowanych przez iPhone: piloty, klawiatury, trackery, bramy IoT.
Cykl życia CBPeripheralManager rozpoczyna się od inicjalizacji i delegata CBPeripheralManagerDelegate. Po potwierdzeniu poweredOn przez peripheralManagerDidUpdateState: publikowane są usługi (addService:) i uruchamiana jest reklama (startAdvertising:). Dane reklamowe CBAdvertisementData obejmują lokalną nazwę (CBAdvertisementDataLocalNameKey), UUID usług (CBAdvertisementDataServiceUUIDsKey) i poziom mocy (CBAdvertisementDataTxPowerLevelKey). Maksymalny rozmiar pakietu reklamowego to 31 bajtów dla BLE 4.0, 251 bajtów dla extended advertising BLE 5.0+.
// Peryferia BLE na iOS przez CBPeripheralManager
class BLEPeripheral: NSObject {
private var peripheralManager: CBPeripheralManager!
let serviceUUID = CBUUID(string: "1234")
let characteristicUUID = CBUUID(string: "5678")
override init() {
super.init()
peripheralManager = CBPeripheralManager(delegate: self, queue: .main)
}
// Opublikuj usługę z charakterystyką
func setupService() {
let characteristic = CBMutableCharacteristic(
type: characteristicUUID,
properties: [.read, .write, .notify],
value: nil,
permissions: [.readable, .writeable]
)
let service = CBMutableService(type: serviceUUID, primary: true)
service.characteristics = [characteristic]
peripheralManager.add(service)
}
// Rozpocznij reklamę
func startAdvertising() {
let advertisementData: [String: Any] = [
CBAdvertisementDataLocalNameKey: "My BLE Device",
CBAdvertisementDataServiceUUIDsKey: [serviceUUID]
]
peripheralManager.startAdvertising(advertisementData)
}
}
// MARK: - CBPeripheralManagerDelegate
extension BLEPeripheral: CBPeripheralManagerDelegate {
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
if peripheral.state == .poweredOn {
setupService()
}
}
func peripheralManager(_ peripheral: CBPeripheralManager,
didAdd service: CBService,
error: Error?) {
if error == nil {
startAdvertising()
}
}
// Obsłuż żądanie odczytu
func peripheralManager(_ peripheral: CBPeripheralManager,
didReceiveRead request: CBATTRequest) {
let data = "CurrentValue".data(using: .utf8)!
request.value = data
peripheralManager.respond(to: request, withResult: .success)
}
// Obsłuż żądanie zapisu
func peripheralManager(_ peripheral: CBPeripheralManager,
didReceiveWrite requests: [CBATTRequest]) {
for request in requests {
if let value = request.value {
print("Write: \(value)")
}
}
peripheralManager.respond(to: requests.first!, withResult: .success)
}
}
Klasa BLEPeripheral tworzy serwer BLE z jedną charakterystyką obsługującą odczyt, zapis i powiadomienia. Po inicjalizacji peripheralManagerDidUpdateState publikuje usługę przez addService:, następnie uruchamia reklamę przez startAdvertising:. Handlerów didReceiveRead i didReceiveWrite odpowiadają na przychodzące żądania GATT od urządzenia centralnego. Do wysyłania powiadomień używana jest metoda updateValue:forCharacteristic:onSubscribedCentrals:.
Operacje GATT (Generic Attribute Profile) — podstawa wymiany danych w Core Bluetooth. Po wykryciu usług i charakterystyk urządzenie centralne może wykonywać trzy typy operacji: odczyt wartości charakterystyki, zapis wartości i subskrypcja powiadomień/indykacji. Każda operacja jest asynchroniczna i zwraca wynik przez odpowiedniego delegata CBPeripheralDelegate.
Odczyt jest wykonywany przez wywołanie readValueForCharacteristic:. Wartość przychodzi w peripheral:didUpdateValueForCharacteristic:error:. Ważne: odczyt zwraca bieżącą wartość z urządzenia, a nie buforowaną. Jeśli urządzenie nie obsługuje odczytu (właściwość .read), wywołanie zwróci błąd. Dla dużych wartości (większych niż MTU) BLE automatycznie fragmentuje i składa dane na poziomie GATT.
Zapis jest wykonywany przez writeValue:forCharacteristic:type:. BLE obsługuje dwa modele zapisu: withResponse (niezawodny, z potwierdzeniem) i withoutResponse (szybki, bez potwierdzenia). Właściwość CBCharacteristic.properties określa dostępne typy zapisu. Maksymalny rozmiar pojedynczego pakietu write jest ograniczony przez MTU: 23 bajty dla BLE 4.0 (20 bajtów danych użytecznych + 3 bajty nagłówka), do 247 bajtów dla BLE 5.0 z extended MTU (MTU 251).
Powiadomienia są aktywowane przez wywołanie setNotifyValue:true forCharacteristic:. Po subskrypcji peryferia automatycznie wysyła aktualizacje przez peripheral:didUpdateValueForCharacteristic: za każdym razem, gdy wartość charakterystyki się zmienia. Do wyłączenia powiadomień wywoływane jest setNotifyValue:false forCharacteristic:. Core Bluetooth automatycznie zarządza deskryptorem CCCD na peryferiach.
| Operacja | Metoda | Delegat | Typ transmisji |
|---|---|---|---|
| Odczyt | readValueForCharacteristic: | didUpdateValueForCharacteristic | Polling (żądanie-odpowiedź) |
| Zapis withResponse | writeValue:forCharacteristic:type:withResponse | didWriteValueForCharacteristic | Z potwierdzeniem |
| Zapis withoutResponse | writeValue:forCharacteristic:type:withoutResponse | Brak delegata | Bez potwierdzenia |
| Powiadomienie | setNotifyValue:true forCharacteristic: | didUpdateNotificationStateForCharacteristic + didUpdateValueForCharacteristic | Push z peryferii |
Tryb tła Core Bluetooth pozwala aplikacji BLE kontynuować skanowanie, utrzymywać połączenia i otrzymywać powiadomienia będąc w tle. Do aktywacji wymagane jest: włączenie capability „Uses Bluetooth LE accessories” w Xcode (Info.plist → Required background modes → App communicates using Core Bluetooth) i dodanie klucza „bluetooth-central” do UIBackgroundModes. Dla roli peryferyjnej — „bluetooth-peripheral”.
State Restoration — mechanizm Core Bluetooth do przywracania stanu połączeń BLE po ponownym uruchomieniu aplikacji przez system iOS. Po aktywacji trybu tła i podaniu restoreIdentifier w inicjalizacji CBCentralManager lub CBPeripheralManager, iOS zapisuje stan stosu BLE przy zakończeniu aplikacji i przywraca go przy następnym uruchomieniu. Delegat centralManager:willRestoreState: otrzymuje słownik z zapisanymi CBPeripheral i oczekującymi połączeniami.
// Konfiguracja Core Bluetooth z State Restoration
class BLECentralWithRestoration: NSObject {
let restoreIdentifier = "com.app.blecentral"
private var centralManager: CBCentralManager!
override init() {
super.init()
let options: [String: Any] = [
CBCentralManagerOptionRestoreIdentifierKey: restoreIdentifier,
CBCentralManagerOptionShowPowerAlertKey: true
]
centralManager = CBCentralManager(delegate: self,
queue: nil,
options: options)
}
}
extension BLECentralWithRestoration: CBCentralManagerDelegate {
// Przywróć stan po restarcie
func centralManager(_ central: CBCentralManager,
willRestoreState dict: [String : Any]) {
if let peripherals = dict[CBCentralManagerRestoredStatePeripheralsKey]
as? [CBPeripheral] {
for peripheral in peripherals {
peripheral.delegate = self
// Przywróć wykrywanie GATT
peripheral.discoverServices(nil)
}
}
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOn {
print("Bluetooth gotowy po przywróceniu")
}
}
}
W konfiguracji BLECentralWithRestoration klucz CBCentralManagerOptionRestoreIdentifierKey aktywuje zapisywanie stanu. Jeśli aplikacja została zakończona przez iOS (np. z powodu braku pamięci), przy następnym uruchomieniu centralManager:willRestoreState: otrzymuje listę wcześniej podłączonych CBPeripheral. Aplikacja przywraca delegatów i wykonuje ponowne wykrywanie usług — użytkownik nie zauważa przerwania połączenia. Bez State Restoration wszystkie sesje BLE są tracone przy zakończeniu aplikacji.
Pełny przykład aplikacji BLE w Swift łączy urządzenie centralne i peryferyjne w jednym projekcie. Aplikacja może pracować w dwóch trybach: wykrywać i podłączać się do urządzeń BLE (Central) lub emulować akcesorium BLE (Peripheral). Poniżej przedstawiono architekturę z wspólnym menedżerem BLE wybierającym rolę przy starcie.
// Uniwersalny menedżer BLE dla centrali i peryferii
class BLEManager {
enum Role {
case central
case peripheral
}
private let role: Role
private var centralManager: CBCentralManager?
private var peripheralManager: CBPeripheralManager?
let advertisedServiceUUID = CBUUID(string: "A001")
init(role: Role) {
self.role = role
switch role {
case .central:
centralManager = CBCentralManager(delegate: nil, queue: .main)
case .peripheral:
peripheralManager = CBPeripheralManager(delegate: nil, queue: .main)
}
}
// Urządzenie centralne: skanowanie
func scanForDevices() {
centralManager?.scanForPeripherals(withServices: nil, options: nil)
}
// Urządzenie peryferyjne: reklama
func advertiseService() {
let data: [String: Any] = [
CBAdvertisementDataServiceUUIDsKey: [advertisedServiceUUID]
]
peripheralManager?.startAdvertising(data)
}
}
// Użycie przy starcie
let isCentral = UserDefaults.standard.bool(forKey: "isCentral")
let manager = BLEManager(role: isCentral ? .central : .peripheral)
if isCentral {
manager.scanForDevices()
} else {
manager.advertiseService()
}
Menedżer BLEManager wybiera rolę przy inicjalizacji i tworzy odpowiedni Manager (CBCentralManager lub CBPeripheralManager). Flaga roli może być przechowywana w UserDefaults lub przekazywana przez serwer konfiguracji. Takie podejście pozwala aplikacji BLE dostosować się do scenariusza użycia: w punkcie sprzedaży iPhone działa jako centrala do skanowania terminali płatniczych, na bramie IoT — jako peryferia do zbierania danych z czujników.
Często zadawane pytania
Core Bluetooth — framework Apple do BLE-rozwoju na iOS, iPadOS i macOS. Udostępnia API do pracy urządzenia centralnego (CBCentralManager) i peryferyjnego (CBPeripheralManager). Obsługuje BLE 4.0–5.4, extended advertising, 2M PHY i LE Audio. Core Bluetooth to jedyne oficjalne API Apple do komunikacji BLE, obowiązkowe dla wszystkich aplikacji iOS pracujących z Bluetooth Low Energy.
CBCentralManager — klasa do pracy w roli urządzenia centralnego: skanuje peryferie BLE, ustanawia połączenia, odczytuje i zapisuje charakterystyki. CBPeripheralManager — klasa do pracy w roli peryferii: publikuje usługi, odpowiada na żądania odczytu/zapisu i wysyła powiadomienia. Jeden iPhone może pracować w dwóch rolach jednocześnie przez różne instancje menedżerów.
Do pracy BLE w tle włącz capability „Uses Bluetooth LE accessories” w Xcode i dodaj klucz „bluetooth-central” do UIBackgroundModes. Dla roli peryferyjnej — „bluetooth-peripheral”. Podaj restoreIdentifier przy inicjalizacji menedżera dla State Restoration. Bez tych ustawień aplikacja w tle nie otrzymuje zdarzeń BLE i traci połączenia.
Główne przyczyny: CBCentralManager.state != .poweredOn (Bluetooth wyłączony lub nieautoryzowany), delegat nie ustawiony, urządzenie poza zasięgiem lub nie wysyła pakietów reklamowych. Sprawdź uprawnienie NSBluetoothAlwaysUsageDescription w Info.plist, status Bluetooth w centralManagerDidUpdateState i upewnij się, że scanForPeripherals jest wywoływany tylko przy .poweredOn.
Tak, Core Bluetooth obsługuje jednoczesne podłączenie do wielu urządzeń BLE. Każdy CBPeripheral jest zarządzany niezależnie przez własnego delegata. iOS ogranicza liczbę jednoczesnych połączeń BLE na poziomie systemu (zwykle 5–7 dla iPhone). Dla scenariuszy 1:N (np. centrum fitness z 10 trackerami) wymagane jest kolejkowanie i cykliczna obsługa peryferii.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również