I have a class that manages a websocket connection, it contains some function that sends data to the server:
func sendRequest() async throws -> // ...
Further the class has a @Published
property connectionStatus
, which shows if a request can be sent:
@Published private(set) var connectionStatus: ConnectionStatus = .notConnected
If there is no connection (i.e. connectionStatus
is .notConnected
), the sendRequest
function throws. If it is .connected, it runs normally. If connectionStatus
is .connecting
however, I want to wait (await
?) at the start of the function until connectionStatus
is either connected or disconnected.
Is there some simple way, using combine to achieve this behaviour? Basically await until a combine publisher turns on a specific value for the first time.
I don't want to check every time if the server is connected or not before trying to send a request, so I need for the caller to be able to call this function independently of connectionStatus
.
You can convert the publisher to an AsyncSequence
using values
. Then find the next value it emits using its async iterator.
@Published private(set) var connectionStatus: ConnectionStatus = .notConnected
func sendRequest() async throws {
if connectionStatus == .notConnected {
throw SomeError()
}
if connectionStatus == .connecting {
var iter = $connectionStatus.values.makeAsyncIterator()
let next = await iter.next()
if next != .connected {
throw SomeError()
}
}
}
If sendRequest
is called by another thread, I think it is possible that connectionStatus
could be changed from .connecting
to .connected
after the connectionStatus == .connecting
, but before $connectionStatus.values.makeAsyncIterator()
, causing the async sequence to "miss" the .connected
value. Since values can only be published from the main actor, I'd suggest isolating sendRequest
to the main actor too.