diff --git a/ios/App/App/CoachPolarBLE.swift b/ios/App/App/CoachPolarBLE.swift index c824db0..cf13bd3 100644 --- a/ios/App/App/CoachPolarBLE.swift +++ b/ios/App/App/CoachPolarBLE.swift @@ -253,15 +253,25 @@ final class PolarPsFtpWriter { var sansPsFtp = 0 // aperçus mais ne portant pas le service FEEE var muettes = 0 // portant FEEE mais dont waitPsFtpReady échoue - // Le scan se fait par fenêtres successives, et non en une seule passe : - // CoreBluetooth peut n'être pas encore `poweredOn` au premier appel — le - // scan ne démarre alors jamais, et une fenêtre unique conclurait à tort - // qu'aucun appareil n'existe. Réessayer couvre ce démarrage. - while Date().timeIntervalSince(debut) < timeout { - let sessions = try await sessionsVues(listener, fenetre: 4) - vues = max(vues, sessions.count) + // ⚠️ UN SEUL ABONNEMENT, SUR TOUTE LA DURÉE — pas des fenêtres. + // + // Le découpage en cycles de 4 s se privait de l'unique émission d'état : + // `bleStateSubject` ne publie que sur CHANGEMENT. Le premier cycle + // réveillait le manager et recevait `.poweredOn` ; aux cycles suivants + // l'état ne changeait plus, donc plus rien n'était émis et le publisher + // restait muet — mesuré le 2026-08-31, y compris après avoir réglé le + // réveil du manager. + // + // Une seule fenêtre, de la durée du timeout, supprime le problème : on + // reçoit l'émission là où elle a lieu, et le scan tourne sans être + // relancé. + do { + let sessions = try await sessionsVues(listener, fenetre: timeout) + vues = sessions.count for session in sessions { - if Date().timeIntervalSince(debut) > timeout { break } + // Marge au-delà du scan : ouvrir une session prend du temps, et + // couper ici ferait échouer la seule montre trouvée. + if Date().timeIntervalSince(debut) > timeout * 2 { break } listener.openSessionDirect(session) guard let client = session.fetchGattClient(BlePsFtpClient.PSFTP_SERVICE) as? BlePsFtpClient else {