Core Bluetooth: architektura i rozwój BLE na iOS

Autor: IT Sectr Opublikowano: 2026-07-16 Czas czytania: 10 min

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 — systemowy framework Apple do BLE-rozwoju na iOS, iPadOS i macOS
  • CBCentralManager — klasa do skanowania i podłączania się do peryferii BLE ze strony urządzenia centralnego
  • CBPeripheralManager — klasa do tworzenia serwera BLE publikującego usługi i charakterystyki
  • Profil GATT — hierarchiczny model usług, charakterystyk i deskryptorów do wymiany danych
  • Tryby tła — Core Bluetooth obsługuje komunikację BLE w tle poprzez delegatów systemowych i state restoration

Czym jest Core Bluetooth: architektura i komponenty

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.

KlasaRolaGłówne metody
CBCentralManagerUrządzenie centralnescanForPeripherals, connect, cancelPeripheralConnection, retrievePeripherals
CBPeripheralZdalna peryferiadiscoverServices, discoverCharacteristics, readValue, writeValue, setNotifyValue
CBPeripheralManagerLokalna peryferiaaddService, removeService, startAdvertising, respondToRequest, updateValue
CBCentralZdalna centralamaximumUpdateValueLength, 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: skanowanie i podłączanie urządzeń BLE

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).

swift
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: tworzenie serwera BLE na iOS

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+.

swift
// 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: odczyt, zapis i powiadomienia

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.

OperacjaMetodaDelegatTyp transmisji
OdczytreadValueForCharacteristic:didUpdateValueForCharacteristicPolling (żądanie-odpowiedź)
Zapis withResponsewriteValue:forCharacteristic:type:withResponsedidWriteValueForCharacteristicZ potwierdzeniem
Zapis withoutResponsewriteValue:forCharacteristic:type:withoutResponseBrak delegataBez potwierdzenia
PowiadomieniesetNotifyValue:true forCharacteristic:didUpdateNotificationStateForCharacteristic + didUpdateValueForCharacteristicPush z peryferii

Tryb tła Core Bluetooth i State Restoration

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.

swift
// 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.

Przykład aplikacji BLE w Swift: centrala i peryferia

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.

swift
// 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

Czym jest Core Bluetooth?

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.

Czym różni się CBCentralManager od CBPeripheralManager?

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.

Jak skonfigurować Core Bluetooth do pracy w tle?

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.

Dlaczego Core Bluetooth nie znajduje urządzeń?

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.

Czy można podłączyć wiele CBPeripheral jednocześnie?

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

  • Core Bluetooth — systemowy framework Apple do BLE-rozwoju z klasami CBCentralManager i CBPeripheralManager
  • CBCentralManager zarządza skanowaniem, podłączaniem i operacjami GATT ze zdalnymi urządzeniami BLE
  • CBPeripheralManager emuluje peryferię BLE z publikacją usług i obsługą przychodzących żądań
  • Profil GATT obejmuje usługi, charakterystyki i deskryptory z operacjami odczytu, zapisu i powiadomień
  • Tryb tła wymaga UIBackgroundModes i restoreIdentifier dla State Restoration
  • MTU BLE ogranicza rozmiar pakietu: 23 bajty dla BLE 4.0, do 251 bajtów dla BLE 5.0+ z extended MTU
  • Swift async/await przez CheckedContinuation upraszcza asynchroniczny kod BLE z delegatami

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.

Omów projekt

Przeczytaj również