Skip to content

Commit 7eb8f82

Browse files
committed
Add heartbeat status
1 parent 2d4855f commit 7eb8f82

File tree

2 files changed

+33
-10
lines changed

2 files changed

+33
-10
lines changed

Sources/Realtime/RealtimeClientV2.swift

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public final class RealtimeClientV2: Sendable {
5555
}
5656

5757
private let statusSubject = AsyncValueSubject<RealtimeClientStatus>(.disconnected)
58-
private let heartbeatSubject = AsyncValueSubject<RealtimeMessageV2?>(nil)
58+
private let heartbeatSubject = AsyncValueSubject<HeartbeatStatus?>(nil)
5959

6060
/// Listen for connection status changes.
6161
///
@@ -73,7 +73,7 @@ public final class RealtimeClientV2: Sendable {
7373
/// Listen for heartbeat checks.
7474
///
7575
/// You can also use ``onHeartbeat(_:)`` for a closure based method.
76-
public var heartbeat: AsyncStream<RealtimeMessageV2> {
76+
public var heartbeat: AsyncStream<HeartbeatStatus> {
7777
heartbeatSubject.values.compactMap { $0 }.eraseToStream()
7878
}
7979

@@ -95,7 +95,7 @@ public final class RealtimeClientV2: Sendable {
9595
///
9696
/// - Nite: Use ``heartbeat`` if you prefer to use Async/Await.
9797
public func onHeartbeat(
98-
_ listener: @escaping @Sendable (RealtimeMessageV2) -> Void
98+
_ listener: @escaping @Sendable (HeartbeatStatus) -> Void
9999
) -> RealtimeSubscription {
100100
let task = heartbeatSubject.onChange { message in
101101
guard let message else { return }
@@ -243,14 +243,15 @@ public final class RealtimeClientV2: Sendable {
243243

244244
private func onClose(code: Int?, reason: String?) {
245245
options.logger?.debug(
246-
"WebSocket closed. Code: \(code?.description ?? "<none>"), Reason: \(reason ?? "<none>")")
246+
"WebSocket closed. Code: \(code?.description ?? "<none>"), Reason: \(reason ?? "<none>")"
247+
)
247248

248249
reconnect()
249250
}
250251

251-
private func reconnect() {
252+
private func reconnect(disconnectReason: String? = nil) {
252253
Task {
253-
disconnect()
254+
disconnect(reason: disconnectReason)
254255
await connect(reconnect: true)
255256
}
256257
}
@@ -294,7 +295,8 @@ public final class RealtimeClientV2: Sendable {
294295
}
295296

296297
@available(
297-
*, deprecated,
298+
*,
299+
deprecated,
298300
message:
299301
"Client handles channels automatically, this method will be removed on the next major release."
300302
)
@@ -396,6 +398,11 @@ public final class RealtimeClientV2: Sendable {
396398
}
397399

398400
private func sendHeartbeat() async {
401+
if status != .connected {
402+
heartbeatSubject.yield(.disconnected)
403+
return
404+
}
405+
399406
let pendingHeartbeatRef: String? = mutableState.withValue {
400407
if $0.pendingHeartbeatRef != nil {
401408
$0.pendingHeartbeatRef = nil
@@ -417,10 +424,12 @@ public final class RealtimeClientV2: Sendable {
417424
payload: [:]
418425
)
419426
)
427+
heartbeatSubject.yield(.sent)
420428
await setAuth()
421429
} else {
422430
options.logger?.debug("Heartbeat timeout")
423-
reconnect()
431+
heartbeatSubject.yield(.timeout)
432+
reconnect(disconnectReason: "heartbeat timeout")
424433
}
425434
}
426435

@@ -478,7 +487,7 @@ public final class RealtimeClientV2: Sendable {
478487

479488
private func onMessage(_ message: RealtimeMessageV2) async {
480489
if message.topic == "phoenix", message.event == "phx_reply" {
481-
heartbeatSubject.yield(message)
490+
heartbeatSubject.yield(message.status == .ok ? .ok : .error)
482491
}
483492

484493
let channel = mutableState.withValue {
@@ -516,7 +525,8 @@ public final class RealtimeClientV2: Sendable {
516525
517526
Error:
518527
\(error)
519-
""")
528+
"""
529+
)
520530
}
521531
}
522532

Sources/Realtime/Types.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,19 @@ public enum RealtimeClientStatus: Sendable, CustomStringConvertible {
8686
}
8787
}
8888

89+
public enum HeartbeatStatus: Sendable {
90+
/// Hearbeat was sent.
91+
case sent
92+
/// Hearbeat was received.
93+
case ok
94+
/// Server responded with an error.
95+
case error
96+
/// Hearbeat wasn't received in time.
97+
case timeout
98+
/// Socket is disconnected.
99+
case disconnected
100+
}
101+
89102
extension HTTPField.Name {
90103
static let apiKey = Self("apiKey")!
91104
}

0 commit comments

Comments
 (0)