CBCentralManager — to centralna klasa frameworka Core Bluetooth w iOS, która zarządza skanowaniem, łączeniem i interakcją z peryferyjnymi urządzeniami BLE. Core Bluetooth (iOS 5+, 2011) zapewnia wysokopoziomową abstrakcję nad stosem BLE na poziomie GATT, ukrywając przed programistą szczegóły Link Layer i HCI. CBCentralManager realizuje rolę Central: skanuje eter przez scanForPeripherals, inicjuje połączenie przez connect, wykrywa usługi przez discoverServices i zarządza przesyłaniem danych. Według Apple Developer Documentation (2024), CBCentralManager obsługuje do 7 jednoczesnych połączeń z urządzeniami BLE na urządzeniach z BLE 5.0.
Najważniejsze
CBCentralManager — to główna klasa Core Bluetooth do implementacji roli Central w architekturze BLE na iOS. Zarządza całym cyklem życia połączenia BLE: od skanowania reklamujących się urządzeń po przesyłanie danych i rozłączanie. CBCentralManager działa asynchronicznie przez delegata CBCentralManagerDelegate, powiadamiając aplikację o zdarzeniach w stosie Bluetooth.
Inicjalizacja CBCentralManager uruchamia proces state restoration: menedżer sprawdza stan Bluetooth na urządzeniu i przywraca poprzednie połączenia, jeśli aplikacja została zamknięta. Proces inicjalizacji może trwać od 50 do 500 ms w zależności od stanu Bluetooth. Aplikacja musi poczekać na wywołanie centralManagerDidUpdateState przed rozpoczęciem jakichkolwiek operacji BLE.
Architektura Core Bluetooth oparta jest na wzorcu Delegation: CBCentralManager deleguje obsługę zdarzeń (wykrywanie urządzeń, łączenie, błędy) do protokołu CBCentralManagerDelegate. Do pracy z konkretnym Peripheral używany jest protokół CBPeripheralDelegate, który powiadamia o wykryciu usług, charakterystyk i otrzymaniu danych. Taki asynchroniczny model zapewnia nieblokujące działanie UI.
CBCentralManager przechodzi przez kilka stanów, które określają, czy stos BLE jest dostępny do pracy. Stan jest przekazywany przez delegata: centralManagerDidUpdateState(_:). Programista musi obsłużyć wszystkie stany — nie tylko poweredOn, ale także przypadki, gdy Bluetooth jest wyłączony lub niedostępny.
| Stan | Znaczenie | Działanie programisty |
|---|---|---|
| .poweredOn | Bluetooth włączony i gotowy | Rozpocznij skanowanie |
| .poweredOff | Bluetooth wyłączony | Pokaż alert użytkownikowi |
| .unauthorized | Brak uprawnień | Poproś o uprawnienia w Ustawieniach |
| .unsupported | Urządzenie nie obsługuje BLE | Ukryj funkcje BLE |
| .unknown | Stan nieokreślony | Czekaj na następną aktualizację |
| .resetting | Bluetooth restartuje się | Czekaj na przywrócenie |
Unauthorized state staje się coraz częstszy od iOS 13+. Począwszy od tej wersji, aplikacja musi mieć uprawnienie NSBluetoothAlwaysUsageDescription w Info.plist. Bez niego centralny menedżer przechodzi w stan .unauthorized i skanowanie jest niemożliwe. Użytkownik może zmienić uprawnienia w Ustawieniach > Prywatność > Bluetooth w dowolnym momencie.
scanForPeripherals(withServices:options:) — główna metoda do uruchamiania skanowania. Parametr withServices przyjmuje tablicę UUID usług do filtrowania: jeśli przekażesz nil, zostaną wykryte wszystkie urządzenia, co znacznie zwiększa zużycie energii. Zaleca się zawsze filtrować po UUID usług, które są potrzebne aplikacji. Opcje skanowania obejmują CBCentralManagerScanOptionAllowDuplicatesKey (powtarzające się powiadomienia o tym samym urządzeniu).
import CoreBluetooth
class BLEController: NSObject,
CBCentralManagerDelegate {
private var centralManager: CBCentralManager!
override init() {
super.init()
centralManager =
CBCentralManager(
delegate: self,
queue: nil
)
}
func startScanning() {
let serviceUUID =
CBUUID("180F") // Serwis Baterii
centralManager.scanForPeripherals(
withServices: [serviceUUID],
options: [
CBCentralManagerScanOptionAllowDuplicatesKey: false
]
)
}
}
Po wykryciu urządzenia wywoływana jest centralManager(_:didDiscover:advertisementData:rssi:). Parametr advertisementData zawiera pełny słownik danych pakietu reklamowego, w tym nazwę urządzenia (CBAdvertisementDataLocalNameKey), UUID usług (CBAdvertisementDataServiceUUIDsKey) i dane producenta (CBAdvertisementDataManufacturerDataKey). RSSI — poziom sygnału w dBm, dostępny w momencie wykrycia.
connect(_:options:) — metoda do ustanawiania połączenia BLE z wykrytym Peripheral. Po wywołaniu connect iOS próbuje połączyć się z urządzeniem. Udane połączenie potwierdzane jest przez wywołanie centralManager(_:didConnect:), błąd — centralManager(_:didFailToConnect:error:). Opcje połączenia obejmują CBConnectPeripheralOptionNotifyOnConnectionKey, CBConnectPeripheralOptionNotifyOnDisconnectionKey i CBConnectPeripheralOptionNotifyOnNotificationKey dla powiadomień w tle.
// Połącz z urządzeniem BLE
func connectToPeripheral(
_ peripheral: CBPeripheral
) {
centralManager.connect(peripheral, options: nil)
// Ustaw delegata dla Peripheral
peripheral.delegate = self
}
// Delegat: udane połączenie
func centralManager(
_ central: CBCentralManager,
didConnect peripheral: CBPeripheral
) {
print("Połączono z " +
"\(peripheral.name ?? "unknown")")
// Rozpocznij wykrywanie usług
peripheral.discoverServices(nil)
}
// Delegat: błąd połączenia
func centralManager(
_ central: CBCentralManager,
didFailToConnect peripheral: CBPeripheral,
error: Error?
) {
print("Connection failed:
\(error?.localizedDescription ?? "")")
}
Limit czasu połączenia na iOS wynosi 30 sekund. Jeśli w tym czasie urządzenie nie odpowiedziało na żądanie połączenia, wywoływane jest didFailToConnect. Na limit czasu wpływają: odległość do urządzenia, zakłócenia, czy urządzenie aktualnie się reklamuje. Przed połączeniem upewnij się, że urządzenie znajduje się w trybie connectable advertising (ADV_IND, a nie ADV_NONCONN_IND).
Po połączeniu należy wykryć usługi (discoverServices) i charakterystyki (discoverCharacteristics) Peripheral. To obowiązkowy krok przed odczytem lub zapisem danych. Proces jest asynchroniczny: discoverServices zwraca wynik przez peripheral(_:didDiscoverServices:), a discoverCharacteristics — przez peripheral(_:didDiscoverCharacteristicsFor:error:).
Zaleca się przekazywanie w discoverServices tablicy interesujących UUID, a nie nil. Filtrowanie przyspiesza wykrywanie i oszczędza energię. Jeśli usługa nie zostanie znaleziona, iOS zgłosi pustą tablicę. Po wykryciu charakterystyk można odczytać ich wartości (readValue), subskrybować powiadomienia (setNotifyValue) lub zapisywać dane (writeValue).
Ważny niuans: MTU jest uzgadniane automatycznie po połączeniu. Aby uzyskać bieżące MTU, użyj peripheral.maximumWriteValueLength(for: .withResponse) lub .withoutResponse. W iOS maksymalne MTU to 512 bajtów dla urządzeń BLE 5.0. Jeśli potrzebujesz przesłać dane większe niż MTU, zaimplementuj fragmentację na poziomie aplikacji.
Skanowanie w tle urządzeń BLE na iOS wymaga specjalnej konfiguracji. Core Bluetooth obsługuje background execution, ale ze znacznymi ograniczeniami. Do pracy w tle konieczne jest: włączenie bluetooth-central w Background Modes w Capabilities projektu, inicjalizacja CBCentralManager z opcją CBCentralManagerOptionRestoreIdentifierKey dla state restoration oraz obsługa zdarzeń centralnego menedżera przy przejściu w tło.
Ograniczenia BLE w tle w iOS: scanForPeripherals bez filtrowania po UUID nie działa w tle. Aplikacja musi określać konkretne UUID usług do skanowania. iOS może opóźniać dostarczanie zdarzeń BLE na nieokreślony czas. Core Bluetooth automatycznie wznawia skanowanie po wykryciu pasującego urządzenia, nawet jeśli aplikacja jest w tle. Limit czasu skanowania w tle: iOS może zatrzymać skanowanie po 10–30 minutach w celu oszczędzania energii.
State Restoration — mechanizm Core Bluetooth, który pozwala przywrócić połączenia BLE po ponownym uruchomieniu aplikacji lub restarcie iOS. Aby go użyć: podaj CBCentralManagerOptionRestoreIdentifierKey przy inicjalizacji, zaimplementuj centralManager(_:willRestoreState:) w delegacie i przywróć listę podłączonych Peripheral z przekazanego słownika. State Restoration — krytyczna funkcjonalność dla aplikacji BLE działających w tle, np. trackerów fitness lub urządzeń medycznych.
CBCentralManager generuje błędy w kilku scenariuszach: połączenie nie powiodło się (didFailToConnect), połączenie zostało zerwane (didDisconnectPeripheral), charakterystyka jest niedostępna do odczytu/zapisu (didWriteValue error). Wszystkie błędy Core Bluetooth są zwracane przez obiekt Error z domeną CBErrorDomain. Najczęstsze kody: CBErrorConnectionTimeout (0x04), CBErrorPeripheralDisconnected (0x07), CBErrorOperationNotSupported (0x0A).
Strategia przywracania połączenia: po otrzymaniu didDisconnectPeripheral sprawdź kod błędu. Jeśli błąd to CBErrorConnectionTimeout lub CBErrorPeripheralDisconnected — zaplanuj automatyczne ponowne połączenie za 1–5 sekund. Jeśli błąd to CBErrorOperationNotSupported — zaloguj i nie próbuj powtarzać operacji. W przypadku krytycznych połączeń (urządzenia medyczne) użyj exponential backoff z maksymalnym interwałem 60 sekund.
// Obsługa rozłączenia z auto-ponownym łączeniem
func centralManager(
_ central: CBCentralManager,
didDisconnectPeripheral peripheral: CBPeripheral,
error: Error?
) {
guard let error = error else {
return // Oczekiwane rozłączenie
}
print("Disconnected: \(error.localizedDescription)")
// Automatyczne ponowne łączenie
if shouldAutoReconnect {
DispatchQueue.main.asyncAfter(
deadline: .now() + reconnectDelay
) {
central.connect(peripheral)
}
}
}
Podczas tworzenia niezawodnej aplikacji BLE na iOS uwzględnij: Core Bluetooth nie gwarantuje dostarczenia wszystkich pakietów przy słabym sygnale. Do niezawodnego przesyłania używaj writeType .withResponse (potwierdzony zapis) i subskrybuj powiadomienia (setNotifyValue), aby otrzymywać dane od Peripheral. Prowadź dziennik błędów do diagnozowania problemów z połączeniem w produkcji.
Często zadawane pytania
Sprawdź stan menedżera przez centralManagerDidUpdateState. Upewnij się, że uprawnienie NSBluetoothAlwaysUsageDescription znajduje się w Info.plist, Bluetooth jest włączony na urządzeniu, a urządzenie peryferyjne reklamuje się z prawidłowym typem (connectable advertising, a nie non-connectable).
Na urządzeniach z BLE 5.0 (iPhone 8 i nowsze) — do 7 jednoczesnych połączeń. Na starszych urządzeniach — do 3–5. Liczba skanowanych urządzeń jest nieograniczona, ale aktywne połączenia mają ścisły limit ustawiony przez Bluetooth Controller.
Zaleca się skanowanie z filtrem po UUID i wyłączanie skanowania po znalezieniu urządzenia. Ciągłe skanowanie rozładowuje baterię: 1 godzina ciągłego skanowania zużywa ~10–15% baterii iPhone'a. Używaj timerów i warunków do zatrzymania skanowania.
CBCentralManager — do skanowania i łączenia z zewnętrznymi urządzeniami BLE (rola Central). CBPeripheralManager — aby twoje urządzenie iOS samo działało jako peryferium BLE (reklamowało usługi). Jedna instancja może być tylko w jednej roli.
Zaimplementuj centralManager(_:didDisconnectPeripheral:error:). Jeśli błąd nie jest nil — zaplanuj automatyczne ponowne połączenie z exponential backoff (1 s → 2 s → 4 s → 8 s → maks. 60 s). Jeśli błąd jest nil — urządzenie rozłączyło się normalnie (np. użytkownik nacisnął przycisk na urządzeniu).
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ż