CBCentralManager est la classe centrale du framework Core Bluetooth dans iOS qui gère le scan, la connexion et l'interaction avec les périphériques BLE. Core Bluetooth (iOS 5+, 2011) fournit une abstraction de haut niveau sur la pile BLE au niveau GATT, cachant au développeur les détails de Link Layer et HCI. CBCentralManager implémente le rôle Central : il scanne les ondes via scanForPeripherals, initie la connexion via connect, découvre les services via discoverServices et gère le transfert de données. Selon la Documentation Développeur Apple (2024), CBCentralManager prend en charge jusqu'à 7 connexions simultanées à des périphériques BLE sur les appareils avec BLE 5.0.
Points Clés
CBCentralManager est la classe principale de Core Bluetooth pour implémenter le rôle Central dans l'architecture BLE sur iOS. Il gère tout le cycle de vie d'une connexion BLE : du scan des appareils en publicité au transfert de données et à la déconnexion. CBCentralManager fonctionne de manière asynchrone via le délégué CBCentralManagerDelegate, notifiant l'application des événements dans la pile Bluetooth.
L'initialisation de CBCentralManager lance le processus de restauration d'état : le gestionnaire vérifie l'état du Bluetooth sur l'appareil et restaure les connexions précédentes si l'application a été fermée. Le processus d'initialisation peut prendre de 50 à 500 ms selon l'état du Bluetooth. L'application doit attendre l'appel centralManagerDidUpdateState avant de commencer toute opération BLE.
L'architecture de Core Bluetooth est construite sur le modèle de Délégation : CBCentralManager délègue le traitement des événements (découverte d'appareils, connexion, erreurs) au protocole CBCentralManagerDelegate. Pour travailler avec un périphérique spécifique, le protocole CBPeripheralDelegate est utilisé, qui notifie de la découverte des services, caractéristiques et données reçues. Ce modèle asynchrone garantit une interface utilisateur non bloquante.
CBCentralManager passe par plusieurs états qui déterminent si la pile BLE est disponible. L'état est transmis via le délégué : centralManagerDidUpdateState(_:). Le développeur doit traiter tous les états — pas seulement poweredOn, mais aussi les cas où Bluetooth est éteint ou indisponible.
| État | Valeur | Action du développeur |
|---|---|---|
| .poweredOn | Bluetooth est allumé et prêt | Lancer le scan |
| .poweredOff | Bluetooth est éteint | Afficher une alerte à l'utilisateur |
| .unauthorized | Pas d'autorisation | Demander l'autorisation dans Réglages |
| .unsupported | L'appareil ne supporte pas BLE | Masquer les fonctions BLE |
| .unknown | État non défini | Attendre la prochaine mise à jour |
| .resetting | Bluetooth redémarre | Attendre la récupération |
L'état non autorisé devient de plus en plus courant depuis iOS 13+. À partir de cette version, l'application doit avoir l'autorisation NSBluetoothAlwaysUsageDescription dans Info.plist. Sans elle, le gestionnaire central passe à l'état .unauthorized, et le scan est impossible. L'utilisateur peut modifier l'autorisation dans Réglages > Confidentialité > Bluetooth à tout moment.
scanForPeripherals(withServices:options:) est la méthode principale pour lancer le scan. Le paramètre withServices accepte un tableau d'UUID de service pour le filtrage : si nil est passé, tous les appareils seront découverts, ce qui augmente considérablement la consommation d'énergie. Il est recommandé de toujours filtrer par les UUID de service nécessaires à l'application. Les options de scan incluent CBCentralManagerScanOptionAllowDuplicatesKey (notifications répétées du même appareil).
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") // Service de batterie
centralManager.scanForPeripherals(
withServices: [serviceUUID],
options: [
CBCentralManagerScanOptionAllowDuplicatesKey: false
]
)
}
}
Lorsqu'un appareil est découvert, centralManager(_:didDiscover:advertisementData:rssi:) est appelé. Le paramètre advertisementData contient le dictionnaire complet des données du paquet de publicité, incluant le nom de l'appareil (CBAdvertisementDataLocalNameKey), les UUID de service (CBAdvertisementDataServiceUUIDsKey) et les données du fabricant (CBAdvertisementDataManufacturerDataKey). RSSI est le niveau de signal en dBm disponible au moment de la découverte.
connect(_:options:) est la méthode pour établir une connexion BLE avec un périphérique découvert. Après avoir appelé connect, iOS tente de se connecter à l'appareil. Une connexion réussie est confirmée via centralManager(_:didConnect:), une erreur via centralManager(_:didFailToConnect:error:). Les options de connexion incluent CBConnectPeripheralOptionNotifyOnConnectionKey, CBConnectPeripheralOptionNotifyOnDisconnectionKey et CBConnectPeripheralOptionNotifyOnNotificationKey pour les notifications en arrière-plan.
// Se connecter au périphérique BLE
func connectToPeripheral(
_ peripheral: CBPeripheral
) {
centralManager.connect(peripheral, options: nil)
// Définir le délégué pour le périphérique
peripheral.delegate = self
}
// Délégué : connexion réussie
func centralManager(
_ central: CBCentralManager,
didConnect peripheral: CBPeripheral
) {
print("Connecté à " +
"\(peripheral.name ?? "unknown")")
// Démarrer la découverte de services
peripheral.discoverServices(nil)
}
// Délégué : erreur de connexion
func centralManager(
_ central: CBCentralManager,
didFailToConnect peripheral: CBPeripheral,
error: Error?
) {
print("Connection failed:
\(error?.localizedDescription ?? "")")
}
Le délai d'attente de connexion sur iOS est de 30 secondes. Si l'appareil n'a pas répondu à la demande de connexion dans ce délai, didFailToConnect est appelé. Le délai d'attente est affecté par : la distance par rapport à l'appareil, les interférences et si l'appareil est actuellement en publicité. Avant de vous connecter, assurez-vous que l'appareil est en mode publicité connectable (ADV_IND, pas ADV_NONCONN_IND).
Après la connexion, vous devez découvrir les services (discoverServices) et caractéristiques (discoverCharacteristics) du périphérique. C'est une étape obligatoire avant de lire ou d'écrire des données. Le processus est asynchrone : discoverServices retourne les résultats via peripheral(_:didDiscoverServices:), et discoverCharacteristics via peripheral(_:didDiscoverCharacteristicsFor:error:).
Il est recommandé de passer un tableau d'UUID pertinents à discoverServices plutôt que nil. Le filtrage accélère la découverte et économise l'énergie. Si un service n'est pas trouvé, iOS signalera un tableau vide. Après la découverte des caractéristiques, vous pouvez lire leurs valeurs (readValue), vous abonner aux notifications (setNotifyValue) ou écrire des données (writeValue).
Une nuance importante : le MTU est négocié automatiquement après la connexion. Pour obtenir le MTU actuel, utilisez peripheral.maximumWriteValueLength(for: .withResponse) ou .withoutResponse. Sur iOS, le MTU maximum est de 512 octets pour les appareils BLE 5.0. Si vous devez transférer des données plus grandes que le MTU, implémentez la fragmentation au niveau de l'application.
Le scan en arrière-plan des appareils BLE sur iOS nécessite une configuration spéciale. Core Bluetooth prend en charge l'exécution en arrière-plan, mais avec des limitations importantes. Pour travailler en arrière-plan, vous devez : activer bluetooth-central dans Background Modes dans les Capacités du projet, initialiser CBCentralManager avec l'option CBCentralManagerOptionRestoreIdentifierKey pour la restauration d'état et gérer les événements du gestionnaire central lors du passage en arrière-plan.
Limitations du BLE en arrière-plan sur iOS : scanForPeripherals sans filtrage par UUID ne fonctionne pas en arrière-plan. L'application doit spécifier des UUID de service concrets pour le scan. iOS peut retarder la livraison des événements BLE indéfiniment. Core Bluetooth reprend automatiquement le scan lorsqu'un appareil correspondant est découvert, même si l'application est en arrière-plan. Délai d'attente pour le scan en arrière-plan : iOS peut arrêter le scan après 10 à 30 minutes pour économiser l'énergie.
La restauration d'état est un mécanisme de Core Bluetooth qui permet de restaurer les connexions BLE après un redémarrage de l'application ou un redémarrage d'iOS. Pour l'utiliser : spécifiez CBCentralManagerOptionRestoreIdentifierKey lors de l'initialisation, implémentez centralManager(_:willRestoreState:) dans le délégué et restaurez la liste des périphériques connectés à partir du dictionnaire passé. La restauration d'état est une fonctionnalité critique pour les applications BLE fonctionnant en arrière-plan, comme les trackers de fitness ou les dispositifs médicaux.
CBCentralManager génère des erreurs dans plusieurs scénarios : échec de connexion (didFailToConnect), connexion perdue (didDisconnectPeripheral), caractéristique non disponible pour lecture/écriture (erreur didWriteValue). Toutes les erreurs Core Bluetooth sont retournées via l'objet Error avec le domaine CBErrorDomain. Les codes les plus courants : CBErrorConnectionTimeout (0x04), CBErrorPeripheralDisconnected (0x07), CBErrorOperationNotSupported (0x0A).
Stratégie de récupération de connexion : lors de la réception de didDisconnectPeripheral, vérifiez le code d'erreur. Si l'erreur est CBErrorConnectionTimeout ou CBErrorPeripheralDisconnected — planifiez une reconnexion automatique dans 1 à 5 secondes. Si l'erreur est CBErrorOperationNotSupported — enregistrez-la et ne réessayez pas l'opération. Pour les connexions critiques (dispositifs médicaux), utilisez le backoff exponentiel avec un intervalle maximum de 60 secondes.
// Gérer la déconnexion avec reconnexion automatique
func centralManager(
_ central: CBCentralManager,
didDisconnectPeripheral peripheral: CBPeripheral,
error: Error?
) {
guard let error = error else {
return // Déconnexion attendue
}
print("Disconnected: \(error.localizedDescription)")
// Reconnexion automatique
if shouldAutoReconnect {
DispatchQueue.main.asyncAfter(
deadline: .now() + reconnectDelay
) {
central.connect(peripheral)
}
}
}
Lors du développement d'une application BLE robuste sur iOS, gardez à l'esprit : Core Bluetooth ne garantit pas la livraison de tous les paquets avec un signal faible. Pour une transmission fiable, utilisez writeType .withResponse (écriture confirmée) et abonnez-vous aux notifications (setNotifyValue) pour recevoir les données du périphérique. Tenez un journal des erreurs pour diagnostiquer les problèmes de connexion en production.
Questions Fréquentes
Vérifiez l'état du gestionnaire via centralManagerDidUpdateState. Assurez-vous que l'autorisation NSBluetoothAlwaysUsageDescription est dans Info.plist, que Bluetooth est activé sur l'appareil et que le périphérique fait de la publicité avec le type correct (publicité connectable, non non-connectable).
Sur les appareils avec BLE 5.0 (iPhone 8 et plus récents) — jusqu'à 7 connexions simultanées. Sur les appareils plus anciens — jusqu'à 3–5. Le nombre d'appareils scannés est illimité, mais les connexions actives ont une limite stricte définie par le contrôleur Bluetooth.
Il est recommandé de scanner avec filtrage par UUID et d'arrêter le scan lorsque l'appareil est trouvé. Le scan continu vide la batterie : 1 heure de scan ininterrompu consomme ~10–15 % de la charge de l'iPhone. Utilisez des minuteries et des conditions pour arrêter le scan.
CBCentralManager est pour scanner et se connecter à des appareils BLE externes (rôle Central). CBPeripheralManager est pour que votre appareil iOS agisse comme un périphérique BLE (publicité de services). Une instance ne peut être que dans un seul rôle.
Implémentez centralManager(_:didDisconnectPeripheral:error:). Si l'erreur n'est pas nil — planifiez une reconnexion automatique avec backoff exponentiel (1 s → 2 s → 4 s → 8 s → max 60 s). Si l'erreur est nil — l'appareil s'est déconnecté normalement (par exemple, l'utilisateur a appuyé sur un bouton de l'appareil).
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi