@@ -13,106 +13,89 @@ import Foundation
13
13
import FoundationNetworking
14
14
#endif
15
15
16
- protocol WebSocketClientProtocol : Sendable {
17
- var status : AsyncStream < WebSocketClient . ConnectionStatus > { get }
16
+ struct WebSocketClient {
17
+ enum ConnectionStatus {
18
+ case open
19
+ case close
20
+ case error( Error )
21
+ }
18
22
19
- func send( _ message: RealtimeMessageV2 ) async throws
20
- func receive( ) -> AsyncThrowingStream < RealtimeMessageV2 , Error >
21
- func connect( )
22
- func cancel( )
23
+ var status : AsyncStream < WebSocketClient . ConnectionStatus >
24
+
25
+ var send : ( _ message: RealtimeMessageV2 ) async throws -> Void
26
+ var receive : ( ) -> AsyncThrowingStream < RealtimeMessageV2 , Error >
27
+ var connect : ( ) async -> Void
28
+ var cancel : ( ) -> Void
23
29
}
24
30
25
- final class WebSocketClient: NSObject , URLSessionWebSocketDelegate , WebSocketClientProtocol ,
26
- @unchecked Sendable
27
- {
28
- struct MutableState {
29
- var session : URLSession ?
30
- var task : URLSessionWebSocketTask ?
31
+ extension WebSocketClient {
32
+ init ( realtimeURL: URL , configuration: URLSessionConfiguration , logger: SupabaseLogger ? ) {
33
+ let client = LiveWebSocketClient (
34
+ realtimeURL: realtimeURL,
35
+ configuration: configuration,
36
+ logger: logger
37
+ )
38
+ self . init (
39
+ status: client. status,
40
+ send: { try await client. send ( $0) } ,
41
+ receive: { client. receive ( ) } ,
42
+ connect: { await client. connect ( ) } ,
43
+ cancel: { client. cancel ( ) }
44
+ )
31
45
}
46
+ }
32
47
48
+ private actor LiveWebSocketClient {
33
49
private let realtimeURL : URL
34
50
private let configuration : URLSessionConfiguration
35
51
private let logger : SupabaseLogger ?
36
52
37
- private let mutableState = LockIsolated ( MutableState ( ) )
38
-
39
- enum ConnectionStatus {
40
- case open
41
- case close
42
- case error( Error )
43
- }
53
+ private var delegate : Delegate ?
54
+ private var session : URLSession ?
55
+ private var task : URLSessionWebSocketTask ?
44
56
45
57
init ( realtimeURL: URL , configuration: URLSessionConfiguration , logger: SupabaseLogger ? ) {
46
58
self . realtimeURL = realtimeURL
47
59
self . configuration = configuration
48
60
49
- let ( stream, continuation) = AsyncStream< ConnectionStatus> . makeStream( )
61
+ let ( stream, continuation) = AsyncStream< WebSocketClient . ConnectionStatus> . makeStream( )
50
62
status = stream
51
63
self . continuation = continuation
52
64
53
65
self . logger = logger
54
- super. init ( )
55
66
}
56
67
57
68
deinit {
58
- mutableState. withValue {
59
- $0. task? . cancel ( )
60
- }
61
-
69
+ task? . cancel ( )
62
70
continuation. finish ( )
63
71
}
64
72
65
- private let continuation : AsyncStream < ConnectionStatus > . Continuation
66
- let status : AsyncStream < ConnectionStatus >
73
+ let continuation : AsyncStream < WebSocketClient . ConnectionStatus > . Continuation
74
+ nonisolated let status : AsyncStream < WebSocketClient . ConnectionStatus >
67
75
68
76
func connect( ) {
69
- mutableState. withValue {
70
- $0. session = URLSession ( configuration: configuration, delegate: self , delegateQueue: nil )
71
- $0. task = $0. session? . webSocketTask ( with: realtimeURL)
72
- $0. task? . resume ( )
73
- }
74
- }
75
-
76
- func cancel( ) {
77
- mutableState. withValue {
78
- $0. task? . cancel ( )
77
+ delegate = Delegate { [ weak self] status in
78
+ self ? . continuation. yield ( status)
79
79
}
80
-
81
- continuation. finish ( )
82
- }
83
-
84
- func urlSession(
85
- _: URLSession ,
86
- webSocketTask _: URLSessionWebSocketTask ,
87
- didOpenWithProtocol _: String ?
88
- ) {
89
- continuation. yield ( . open)
80
+ session = URLSession ( configuration: configuration, delegate: delegate, delegateQueue: nil )
81
+ task = session? . webSocketTask ( with: realtimeURL)
82
+ task? . resume ( )
90
83
}
91
84
92
- func urlSession(
93
- _: URLSession ,
94
- webSocketTask _: URLSessionWebSocketTask ,
95
- didCloseWith _: URLSessionWebSocketTask . CloseCode ,
96
- reason _: Data ?
97
- ) {
98
- continuation. yield ( . close)
85
+ nonisolated func cancel( ) {
86
+ Task { await _cancel ( ) }
99
87
}
100
88
101
- func urlSession(
102
- _: URLSession ,
103
- task _: URLSessionTask ,
104
- didCompleteWithError error: Error ?
105
- ) {
106
- if let error {
107
- continuation. yield ( . error( error) )
108
- }
89
+ private func _cancel( ) {
90
+ task? . cancel ( )
91
+ continuation. finish ( )
109
92
}
110
93
111
- func receive( ) -> AsyncThrowingStream < RealtimeMessageV2 , Error > {
94
+ nonisolated func receive( ) -> AsyncThrowingStream < RealtimeMessageV2 , Error > {
112
95
let ( stream, continuation) = AsyncThrowingStream < RealtimeMessageV2 , Error > . makeStream ( )
113
96
114
97
Task {
115
- while let message = try await self . mutableState . task? . receive ( ) {
98
+ while let message = try await self . task? . receive ( ) {
116
99
do {
117
100
switch message {
118
101
case let . string( stringMessage) :
@@ -144,6 +127,41 @@ final class WebSocketClient: NSObject, URLSessionWebSocketDelegate, WebSocketCli
144
127
let string = String ( decoding: data, as: UTF8 . self)
145
128
146
129
logger? . verbose ( " Sending message: \( string) " )
147
- try await mutableState. task? . send ( . string( string) )
130
+ try await task? . send ( . string( string) )
131
+ }
132
+
133
+ final class Delegate : NSObject , URLSessionWebSocketDelegate {
134
+ let onStatusChange : ( _ status: WebSocketClient . ConnectionStatus ) -> Void
135
+
136
+ init ( onStatusChange: @escaping ( _ status: WebSocketClient . ConnectionStatus ) -> Void ) {
137
+ self . onStatusChange = onStatusChange
138
+ }
139
+
140
+ func urlSession(
141
+ _: URLSession ,
142
+ webSocketTask _: URLSessionWebSocketTask ,
143
+ didOpenWithProtocol _: String ?
144
+ ) {
145
+ onStatusChange ( . open)
146
+ }
147
+
148
+ func urlSession(
149
+ _: URLSession ,
150
+ webSocketTask _: URLSessionWebSocketTask ,
151
+ didCloseWith _: URLSessionWebSocketTask . CloseCode ,
152
+ reason _: Data ?
153
+ ) {
154
+ onStatusChange ( . close)
155
+ }
156
+
157
+ func urlSession(
158
+ _: URLSession ,
159
+ task _: URLSessionTask ,
160
+ didCompleteWithError error: Error ?
161
+ ) {
162
+ if let error {
163
+ onStatusChange ( . error( error) )
164
+ }
165
+ }
148
166
}
149
167
}
0 commit comments