Skip to content

Commit

Permalink
Watch for connection loss during connect (#668)
Browse files Browse the repository at this point in the history
Closes #637
  • Loading branch information
twyatt authored Apr 18, 2024
1 parent 4a6aabd commit 7a58126
Showing 1 changed file with 24 additions and 2 deletions.
26 changes: 24 additions & 2 deletions core/src/appleMain/kotlin/CBPeripheralCoreBluetoothPeripheral.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNot
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
Expand Down Expand Up @@ -148,6 +149,10 @@ internal class CBPeripheralCoreBluetoothPeripheral(
logger.info { message = "Connecting" }
_state.value = State.Connecting.Bluetooth

val failureWatcher = centralManager.delegate
.connectionState
.watchForConnectionFailureIn(scope)

try {
_connection.value = centralManager.connectPeripheral(
scope,
Expand All @@ -156,8 +161,6 @@ internal class CBPeripheralCoreBluetoothPeripheral(
logging,
)

// fixme: Handle centralManager:didFailToConnectPeripheral:error:
// https://developer.apple.com/documentation/corebluetooth/cbcentralmanagerdelegate/1518988-centralmanager
suspendUntilOrThrow<State.Connecting.Services>()
discoverServices()
onServicesDiscovered(ServicesDiscoveredPeripheral(this@CBPeripheralCoreBluetoothPeripheral))
Expand All @@ -170,6 +173,8 @@ internal class CBPeripheralCoreBluetoothPeripheral(
val failure = e.unwrapCancellationCause()
logger.error(failure) { message = "Failed to connect" }
throw failure
} finally {
failureWatcher.cancel()
}

logger.info { message = "Connected" }
Expand Down Expand Up @@ -199,6 +204,23 @@ internal class CBPeripheralCoreBluetoothPeripheral(
}
.launchIn(scope)

private fun Flow<ConnectionEvent>.watchForConnectionFailureIn(scope: CoroutineScope) =
filter { identifier -> identifier == cbPeripheral.identifier }
.filterNot { event -> event is DidConnect }
.onEach { event ->
val error = when (event) {
is DidFailToConnect -> event.error
is DidDisconnect -> event.error
else -> null
}
val failure = error
?.let { ": ${it.toStatus()} (${it.localizedDescription})" }
.orEmpty()
logger.info { message = "Disconnected$failure" }
throw ConnectionLostException("$this disconnected$failure")
}
.launchIn(scope)

override suspend fun disconnect() {
closeConnection()
suspendUntil<State.Disconnected>()
Expand Down

0 comments on commit 7a58126

Please sign in to comment.