Skip to content

Commit 377a910

Browse files
committed
Add ping-type message logic
1 parent e85e1ea commit 377a910

File tree

1 file changed

+67
-8
lines changed

1 file changed

+67
-8
lines changed

Sources/ObservableWebSocketClient/Interface/ObservableWebSocketClient.swift

Lines changed: 67 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ public final class ObservableWebSocketClient: Identifiable, Equatable, Codable,
2525

2626
public let websocketURL: URL
2727

28+
private var pingTimer: Timer?
29+
private let pingTimerInterval: TimeInterval?
30+
private let pingMessage: String?
31+
private let pingMessageWithGeneratedId: ((String) -> String)?
32+
2833
private(set) var service: ObservableWebSocketService
2934

3035
var cancellables = Set<AnyCancellable>()
@@ -34,23 +39,77 @@ public final class ObservableWebSocketClient: Identifiable, Equatable, Codable,
3439
/// Creates an `ObservableWebSocketClient` instance.
3540
///
3641
/// - Parameters:
42+
/// - id: Optional unique ID of the instance. If absent, an instance of `UUID` will be used instead.
3743
/// - websocketURL: The WebSocket `URL` to connect to, starting with `wss`.
38-
/// E.g., `wss://endpoint.com`
39-
public convenience init(websocketURL: URL) {
40-
self.init(websocketURL: websocketURL,
41-
message: nil,
42-
error: nil)
43-
}
44-
44+
/// For example:
45+
/// ```
46+
/// wss://endpoint.com
47+
/// ```
48+
/// - message: Optional `CodableWebSocketMessage`. Useful for mocking the instance's state.
49+
/// - error: Optional `ObservableWebSocketClientError`. Useful for mocking the instance's state.
50+
/// - pingTimerInterval: The value passed in (`TimeInterval`) will cause a timer to
51+
/// continuously send ping-type messages to the WS server, keeping the connection alive.
52+
/// - pingMessage: The ping-type `String` message.
53+
/// For example:
54+
/// ```
55+
/// "{\"id\": \"\(myId)\", \"type\": \"ping\"}"
56+
/// ```
57+
/// - pingMessageWithGeneratedId: The ping-type `String` message, including
58+
/// a dynamically generated ID. The closure (`(String) -> String`) takes a `String`
59+
/// (the generated ID) and returns a modified message string incorporating that ID.
60+
/// Notice that if `pingMessage` is also passed in, `pingMessage` will be used instead
61+
/// (causing `pingMessageWithGeneratedId` to be ignored). Usage Example:
62+
/// ```
63+
/// webSocketClient = .init(
64+
/// websocketURL: someWebSocketURL,
65+
/// pingTimerInterval: 18,
66+
/// pingMessageWithGeneratedId: { generatedId in
67+
/// "{\"id\": \"\(generatedId)\", \"type\": \"ping\"}"
68+
/// }
69+
/// )
70+
/// // The above will send the WS server (every 18 seconds)
71+
/// // a message like this:
72+
/// // {"id": "some-random-uuid", "type": "ping"}
73+
/// ```
4574
public init(id: UUID = .init(),
4675
websocketURL: URL,
4776
message: CodableWebSocketMessage? = nil,
48-
error: ObservableWebSocketClientError? = nil) {
77+
error: ObservableWebSocketClientError? = nil,
78+
pingTimerInterval: TimeInterval? = nil,
79+
pingMessage: String? = nil,
80+
pingMessageWithGeneratedId: ((String) -> String)? = nil) {
4981
self.id = id
5082
self.websocketURL = websocketURL
5183
self.codableMessage = message
5284
self.error = error
85+
self.pingTimerInterval = pingTimerInterval
86+
self.pingMessage = pingMessage
87+
self.pingMessageWithGeneratedId = pingMessageWithGeneratedId
5388
self.service = ObservableWebSocketService(url: websocketURL)
89+
5490
observeWebSocketConnection()
91+
startPingTimer()
92+
}
93+
94+
deinit {
95+
pingTimer?.invalidate()
96+
}
97+
}
98+
99+
// MARK: - Private
100+
101+
private extension ObservableWebSocketClient {
102+
func startPingTimer() {
103+
if let pingTimerInterval,
104+
pingMessage?.isEmpty == false || pingMessageWithGeneratedId != nil {
105+
pingTimer = Timer.scheduledTimer(
106+
withTimeInterval: pingTimerInterval, repeats: true) { [weak self] _ in
107+
if let message = self?.pingMessage {
108+
self?.sendMessage(message)
109+
} else if let messageWithGeneratedId = self?.pingMessageWithGeneratedId {
110+
self?.sendMessageWithGeneratedId(messageWithGeneratedId)
111+
}
112+
}
113+
}
55114
}
56115
}

0 commit comments

Comments
 (0)