Le scan du SDK ne démarre pas, et le message le dira au lieu d'accuser la montre

43 appareils vus par un scan CoreBluetooth nu, 0 remonté par le SDK Polar. La
radio, l'autorisation et la montre sont hors de cause — c'est notre usage du SDK.

Trois suspects écartés sur les sources plutôt que supposés, et écrits dans le
runbook pour que personne ne les revérifie : servicesToScanFor à nil est aussi
ce que fait PolarBleApiImpl ; un scanPreFilter nil ACCEPTE les appareils (le
`if let` échoue, le rejet est sauté) ; et addClient() suffit à lancer le scan,
scanningNeeded() rendant vrai dès que clientCount != 0.

Reste une hypothèse : search() commence par
`monitorBleState().filter { $0 == .poweredOn }`. Si cet état n'arrive jamais, le
publisher ne dit RIEN — ni valeur, ni complétion, ni erreur — et le timeout
conclut « aucun appareil » alors qu'on n'a jamais cherché. Ce sont deux
diagnostics opposés que le même message couvrait.

L'instrumentation note donc si le publisher a parlé, et relaie une éventuelle
erreur du SDK telle quelle. Trois messages distincts en sortent, trois tests les
verrouillent.

Piste notée pour la reprise : le SDK pose powerStateObserver et
deviceSessionStateObserver sur son listener et passe identifier: 0 ; nous ne
posons aucun observateur et passons 1.

80 tests au vert sur tests-linux.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sylvain Bettinelli
2026-08-31 13:06:15 +00:00
parent 14133efba9
commit 3de149591e
4 changed files with 101 additions and 7 deletions

View File

@@ -109,7 +109,9 @@ public enum PolarPftpError: Error, CustomStringConvertible, Equatable {
/// Le SDK n'a remonté aucune session, mais CoreBluetooth, interrogé
/// directement, a vu `vusParCoreBluetooth` appareils. Deux diagnostics
/// opposés selon ce nombre.
case sdkSilencieux(vusParCoreBluetooth: Int)
case sdkSilencieux(vusParCoreBluetooth: Int,
publisherAParle: Bool = true,
erreurSdk: String? = nil)
/// Un échec de connexion a plusieurs causes OPPOSÉES, et un message
/// unique les confond c'est ce qui s'est produit au premier essai du
@@ -123,13 +125,25 @@ public enum PolarPftpError: Error, CustomStringConvertible, Equatable {
case .bluetoothUnusable(let why):
return why
case .sdkSilencieux(0):
case .sdkSilencieux(_, false, _):
// Le publisher n'a JAMAIS rien émis : `search()` s'arrête sur
// `monitorBleState().filter { $0 == .poweredOn }`. Le scan n'a donc
// pas démarré ce n'est pas « aucun appareil », c'est « on n'a
// jamais cherché ».
return "le SDK Polar n'a jamais signalé que le Bluetooth était prêt : "
+ "son scan n'a pas démarré. Ce n'est pas la montre. "
+ "Piste : le listener du SDK n'obtient pas l'état poweredOn."
case .sdkSilencieux(_, _, .some(let erreur)):
return "le SDK Polar a répondu par une erreur : \(erreur)"
case .sdkSilencieux(0, _, _):
return "aucun appareil Bluetooth alentour, même en scan direct. "
+ "La radio fonctionne mais ne voit rien : montre éteinte, hors "
+ "de portée, ou déjà connectée à un autre appareil de façon "
+ "exclusive."
case .sdkSilencieux(let bruts):
case .sdkSilencieux(let bruts, _, _):
return "\(bruts) appareil(s) Bluetooth vus en scan direct, mais le SDK "
+ "Polar n'en remonte aucun. La radio va bien : le problème est "
+ "dans l'intégration du SDK, pas dans la montre ni dans l'iPhone."