@@ -20,8 +20,13 @@ public final class RealtimeClientV2: Sendable {
2020 var accessToken : String ?
2121 var ref = 0
2222 var pendingHeartbeatRef : Int ?
23+
24+ /// Long-running task that keeps sending heartbeat messages.
2325 var heartbeatTask : Task < Void , Never > ?
26+
27+ /// Long-running task for listening for incoming messages from WebSocket.
2428 var messageTask : Task < Void , Never > ?
29+
2530 var connectionTask : Task < Void , Never > ?
2631 var channels : [ String : RealtimeChannelV2 ] = [ : ]
2732 var sendBuffer : [ @Sendable ( ) async -> Void ] = [ ]
@@ -34,13 +39,14 @@ public final class RealtimeClientV2: Sendable {
3439 let http : any HTTPClientType
3540 let apikey : String ?
3641
42+ /// All managed channels indexed by their topics.
3743 public var channels : [ String : RealtimeChannelV2 ] {
3844 mutableState. channels
3945 }
4046
4147 private let statusEventEmitter = EventEmitter < RealtimeClientStatus > ( initialEvent: . disconnected)
4248
43- /// AsyncStream that emits when connection status change .
49+ /// Listen for connection status changes .
4450 ///
4551 /// You can also use ``onStatusChange(_:)`` for a closure based method.
4652 public var statusChange : AsyncStream < RealtimeClientStatus > {
@@ -198,6 +204,13 @@ public final class RealtimeClientV2: Sendable {
198204 await connect ( reconnect: true )
199205 }
200206
207+ /// Creates a new channel and bind it to this client.
208+ /// - Parameters:
209+ /// - topic: Channel's topic.
210+ /// - options: Configuration options for the channel.
211+ /// - Returns: Channel instance.
212+ ///
213+ /// - Note: This method doesn't subscribe to the channel, call ``RealtimeChannelV2/subscribe()`` on the returned channel instance.
201214 public func channel(
202215 _ topic: String ,
203216 options: @Sendable ( inout RealtimeChannelConfig ) -> Void = { _ in }
@@ -223,6 +236,9 @@ public final class RealtimeClientV2: Sendable {
223236 }
224237 }
225238
239+ /// Unsubscribe and removes channel.
240+ ///
241+ /// If there is no channel left, client is disconnected.
226242 public func removeChannel( _ channel: RealtimeChannelV2 ) async {
227243 if channel. status == . subscribed {
228244 await channel. unsubscribe ( )
@@ -238,6 +254,7 @@ public final class RealtimeClientV2: Sendable {
238254 }
239255 }
240256
257+ /// Unsubscribes and removes all channels.
241258 public func removeAllChannels( ) async {
242259 await withTaskGroup ( of: Void . self) { group in
243260 for channel in channels. values {
@@ -327,6 +344,7 @@ public final class RealtimeClientV2: Sendable {
327344 }
328345 }
329346
347+ /// Disconnects client.
330348 public func disconnect( ) {
331349 options. logger? . debug ( " Closing WebSocket connection " )
332350 mutableState. withValue {
@@ -388,13 +406,14 @@ public final class RealtimeClientV2: Sendable {
388406 try Task . checkCancellation ( )
389407 try await self ? . ws. send ( message)
390408 } catch {
391- self ? . options. logger? . error ( """
392- Failed to send message:
393- \( message)
394-
395- Error:
396- \( error)
397- """ )
409+ self ? . options. logger? . error (
410+ """
411+ Failed to send message:
412+ \( message)
413+
414+ Error:
415+ \( error)
416+ """ )
398417 }
399418 }
400419
@@ -470,3 +489,31 @@ public final class RealtimeClientV2: Sendable {
470489 url. appendingPathComponent ( " api/broadcast " )
471490 }
472491}
492+
493+ import Network
494+
495+ final class NetworkMonitor : @unchecked Sendable {
496+ static let shared = NetworkMonitor ( )
497+
498+ private let monitor : NWPathMonitor
499+ private let queue = DispatchQueue ( label: " NetworkMonitor " )
500+
501+ private( set) var isConnected : Bool = false
502+
503+ private init ( ) {
504+ monitor = NWPathMonitor ( )
505+ }
506+
507+ func start( _ onChange: ( @Sendable ( ) -> Void ) ? = nil ) {
508+ monitor. pathUpdateHandler = { [ weak self] path in
509+ self ? . isConnected = path. status != . unsatisfied
510+ onChange ? ( )
511+ }
512+
513+ monitor. start ( queue: queue)
514+ }
515+
516+ func stop( ) {
517+ monitor. cancel ( )
518+ }
519+ }
0 commit comments