Skip to content

Commit a037987

Browse files
committed
feat(apple): realtime heartbeat
1 parent 3ca2123 commit a037987

File tree

1 file changed

+37
-0
lines changed

1 file changed

+37
-0
lines changed

templates/swift/Sources/Services/Realtime.swift.twig

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@ open class Realtime : Service {
77

88
private let TYPE_ERROR = "error"
99
private let TYPE_EVENT = "event"
10+
private let TYPE_PONG = "pong"
1011
private let DEBOUNCE_NANOS = 1_000_000
12+
private let HEARTBEAT_INTERVAL: UInt64 = 20_000_000_000 // 20 seconds in nanoseconds
1113

1214
private var socketClient: WebSocketClient? = nil
1315
private var activeChannels = Set<String>()
1416
private var activeSubscriptions = [Int: RealtimeCallback]()
17+
private var heartbeatTask: Task<Void, Error>? = nil
1518

1619
let connectSync = DispatchQueue(label: "ConnectSync")
1720

@@ -20,6 +23,33 @@ open class Realtime : Service {
2023
private var subscriptionsCounter = 0
2124
private var reconnect = true
2225

26+
private func startHeartbeat() {
27+
stopHeartbeat()
28+
heartbeatTask = Task {
29+
do {
30+
while !Task.isCancelled {
31+
if let client = socketClient, client.isConnected {
32+
let pingMessage = ["type": "ping"]
33+
if let jsonData = try JSONSerialization.data(withJSONObject: pingMessage),
34+
let jsonString = String(data: jsonData, encoding: .utf8) {
35+
client.send(text: jsonString)
36+
}
37+
}
38+
try await Task.sleep(nanoseconds: HEARTBEAT_INTERVAL)
39+
}
40+
} catch {
41+
if !Task.isCancelled {
42+
print("Heartbeat task failed: \(error.localizedDescription)")
43+
}
44+
}
45+
}
46+
}
47+
48+
private func stopHeartbeat() {
49+
heartbeatTask?.cancel()
50+
heartbeatTask = nil
51+
}
52+
2353
private func createSocket() async throws {
2454
guard activeChannels.count > 0 else {
2555
reconnect = false
@@ -50,6 +80,8 @@ open class Realtime : Service {
5080
}
5181

5282
private func closeSocket() async throws {
83+
stopHeartbeat()
84+
5385
guard let client = socketClient,
5486
let group = client.threadGroup else {
5587
return
@@ -163,6 +195,7 @@ extension Realtime: WebSocketClientDelegate {
163195

164196
public func onOpen(channel: Channel) {
165197
self.reconnectAttempts = 0
198+
startHeartbeat()
166199
}
167200

168201
public func onMessage(text: String) {
@@ -172,13 +205,16 @@ extension Realtime: WebSocketClientDelegate {
172205
switch type {
173206
case TYPE_ERROR: try! handleResponseError(from: json)
174207
case TYPE_EVENT: handleResponseEvent(from: json)
208+
case TYPE_PONG: break // Handle pong response if needed
175209
default: break
176210
}
177211
}
178212
}
179213
}
180214

181215
public func onClose(channel: Channel, data: Data) async throws {
216+
stopHeartbeat()
217+
182218
if (!reconnect) {
183219
reconnect = true
184220
return
@@ -196,6 +232,7 @@ extension Realtime: WebSocketClientDelegate {
196232
}
197233

198234
public func onError(error: Swift.Error?, status: HTTPResponseStatus?) {
235+
stopHeartbeat()
199236
print(error?.localizedDescription ?? "Unknown error")
200237
}
201238

0 commit comments

Comments
 (0)