@@ -2,17 +2,15 @@ import Foundation
22import GraphQL
33
44/// Client is an open-ended implementation of the client side of the protocol. It parses and adds callbacks for each type of server respose.
5- public class Client < InitPayload: Equatable & Codable > {
6- // We keep this weak because we strongly inject this object into the messenger callback
7- weak var messenger : Messenger ?
5+ public actor Client < InitPayload: Equatable & Codable > {
6+ let messenger : Messenger
87
9- var onConnectionError : ( ConnectionErrorResponse , Client ) async throws -> Void = { _, _ in }
10- var onConnectionAck : ( ConnectionAckResponse , Client ) async throws -> Void = { _, _ in }
11- var onConnectionKeepAlive : ( ConnectionKeepAliveResponse , Client ) async throws -> Void = { _, _ in }
12- var onData : ( DataResponse , Client ) async throws -> Void = { _, _ in }
13- var onError : ( ErrorResponse , Client ) async throws -> Void = { _, _ in }
14- var onComplete : ( CompleteResponse , Client ) async throws -> Void = { _, _ in }
15- var onMessage : ( String , Client ) async throws -> Void = { _, _ in }
8+ let onConnectionError : ( ConnectionErrorResponse , Client ) async throws -> Void
9+ let onConnectionAck : ( ConnectionAckResponse , Client ) async throws -> Void
10+ let onConnectionKeepAlive : ( ConnectionKeepAliveResponse , Client ) async throws -> Void
11+ let onData : ( DataResponse , Client ) async throws -> Void
12+ let onError : ( ErrorResponse , Client ) async throws -> Void
13+ let onComplete : ( CompleteResponse , Client ) async throws -> Void
1614
1715 let encoder = GraphQLJSONEncoder ( )
1816 let decoder = JSONDecoder ( )
@@ -21,120 +19,98 @@ public class Client<InitPayload: Equatable & Codable> {
2119 ///
2220 /// - Parameters:
2321 /// - messenger: The messenger to bind the client to.
22+ /// - onConnectionError: The callback run on receipt of a `connection_error` message
23+ /// - onConnectionAck: The callback run on receipt of a `connection_ack` message
24+ /// - onConnectionKeepAlive: The callback run on receipt of a `connection_ka` message
25+ /// - onData: The callback run on receipt of a `data` message
26+ /// - onError: The callback run on receipt of an `error` message
27+ /// - onComplete: The callback run on receipt of a `complete` message
2428 public init (
25- messenger: Messenger
29+ messenger: Messenger ,
30+ onConnectionError: @escaping ( ConnectionErrorResponse , Client ) async throws -> Void = { _, _ in } ,
31+ onConnectionAck: @escaping ( ConnectionAckResponse , Client ) async throws -> Void = { _, _ in } ,
32+ onConnectionKeepAlive: @escaping ( ConnectionKeepAliveResponse , Client ) async throws -> Void = { _, _ in } ,
33+ onData: @escaping ( DataResponse , Client ) async throws -> Void = { _, _ in } ,
34+ onError: @escaping ( ErrorResponse , Client ) async throws -> Void = { _, _ in } ,
35+ onComplete: @escaping ( CompleteResponse , Client ) async throws -> Void = { _, _ in }
2636 ) {
2737 self . messenger = messenger
28- messenger. onReceive { message in
29- try await self . onMessage ( message, self )
38+ self . onConnectionError = onConnectionError
39+ self . onConnectionAck = onConnectionAck
40+ self . onConnectionKeepAlive = onConnectionKeepAlive
41+ self . onData = onData
42+ self . onError = onError
43+ self . onComplete = onComplete
44+ }
3045
46+ /// Listen and react to the provided async sequence of server messages. This function will block until the stream is completed.
47+ /// - Parameter incoming: The server message sequence that the client should react to.
48+ public func listen< A: AsyncSequence & Sendable > ( to incoming: A ) async throws -> Void where A. Element == String {
49+ for try await message in incoming {
3150 // Detect and ignore error responses.
3251 if message. starts ( with: " 44 " ) {
3352 // TODO: Determine what to do with returned error messages
3453 return
3554 }
3655
3756 guard let json = message. data ( using: . utf8) else {
38- try await self . error ( . invalidEncoding( ) )
57+ try await error ( . invalidEncoding( ) )
3958 return
4059 }
4160
4261 let response : Response
4362 do {
44- response = try self . decoder. decode ( Response . self, from: json)
63+ response = try decoder. decode ( Response . self, from: json)
4564 } catch {
4665 try await self . error ( . noType( ) )
4766 return
4867 }
4968
5069 switch response. type {
5170 case . GQL_CONNECTION_ERROR:
52- guard let connectionErrorResponse = try ? self . decoder. decode ( ConnectionErrorResponse . self, from: json) else {
53- try await self . error ( . invalidResponseFormat( messageType: . GQL_CONNECTION_ERROR) )
71+ guard let connectionErrorResponse = try ? decoder. decode ( ConnectionErrorResponse . self, from: json) else {
72+ try await error ( . invalidResponseFormat( messageType: . GQL_CONNECTION_ERROR) )
5473 return
5574 }
56- try await self . onConnectionError ( connectionErrorResponse, self )
75+ try await onConnectionError ( connectionErrorResponse, self )
5776 case . GQL_CONNECTION_ACK:
58- guard let connectionAckResponse = try ? self . decoder. decode ( ConnectionAckResponse . self, from: json) else {
59- try await self . error ( . invalidResponseFormat( messageType: . GQL_CONNECTION_ERROR) )
77+ guard let connectionAckResponse = try ? decoder. decode ( ConnectionAckResponse . self, from: json) else {
78+ try await error ( . invalidResponseFormat( messageType: . GQL_CONNECTION_ERROR) )
6079 return
6180 }
62- try await self . onConnectionAck ( connectionAckResponse, self )
81+ try await onConnectionAck ( connectionAckResponse, self )
6382 case . GQL_CONNECTION_KEEP_ALIVE:
64- guard let connectionKeepAliveResponse = try ? self . decoder. decode ( ConnectionKeepAliveResponse . self, from: json) else {
65- try await self . error ( . invalidResponseFormat( messageType: . GQL_CONNECTION_KEEP_ALIVE) )
83+ guard let connectionKeepAliveResponse = try ? decoder. decode ( ConnectionKeepAliveResponse . self, from: json) else {
84+ try await error ( . invalidResponseFormat( messageType: . GQL_CONNECTION_KEEP_ALIVE) )
6685 return
6786 }
68- try await self . onConnectionKeepAlive ( connectionKeepAliveResponse, self )
87+ try await onConnectionKeepAlive ( connectionKeepAliveResponse, self )
6988 case . GQL_DATA:
70- guard let nextResponse = try ? self . decoder. decode ( DataResponse . self, from: json) else {
71- try await self . error ( . invalidResponseFormat( messageType: . GQL_DATA) )
89+ guard let nextResponse = try ? decoder. decode ( DataResponse . self, from: json) else {
90+ try await error ( . invalidResponseFormat( messageType: . GQL_DATA) )
7291 return
7392 }
74- try await self . onData ( nextResponse, self )
93+ try await onData ( nextResponse, self )
7594 case . GQL_ERROR:
76- guard let errorResponse = try ? self . decoder. decode ( ErrorResponse . self, from: json) else {
77- try await self . error ( . invalidResponseFormat( messageType: . GQL_ERROR) )
95+ guard let errorResponse = try ? decoder. decode ( ErrorResponse . self, from: json) else {
96+ try await error ( . invalidResponseFormat( messageType: . GQL_ERROR) )
7897 return
7998 }
80- try await self . onError ( errorResponse, self )
99+ try await onError ( errorResponse, self )
81100 case . GQL_COMPLETE:
82- guard let completeResponse = try ? self . decoder. decode ( CompleteResponse . self, from: json) else {
83- try await self . error ( . invalidResponseFormat( messageType: . GQL_COMPLETE) )
101+ guard let completeResponse = try ? decoder. decode ( CompleteResponse . self, from: json) else {
102+ try await error ( . invalidResponseFormat( messageType: . GQL_COMPLETE) )
84103 return
85104 }
86- try await self . onComplete ( completeResponse, self )
105+ try await onComplete ( completeResponse, self )
87106 default :
88- try await self . error ( . invalidType( ) )
107+ try await error ( . invalidType( ) )
89108 }
90109 }
91110 }
92111
93- /// Define the callback run on receipt of a `connection_error` message
94- /// - Parameter callback: The callback to assign
95- public func onConnectionError( _ callback: @escaping ( ConnectionErrorResponse , Client ) async throws -> Void ) {
96- onConnectionError = callback
97- }
98-
99- /// Define the callback run on receipt of a `connection_ack` message
100- /// - Parameter callback: The callback to assign
101- public func onConnectionAck( _ callback: @escaping ( ConnectionAckResponse , Client ) async throws -> Void ) {
102- onConnectionAck = callback
103- }
104-
105- /// Define the callback run on receipt of a `connection_ka` message
106- /// - Parameter callback: The callback to assign
107- public func onConnectionKeepAlive( _ callback: @escaping ( ConnectionKeepAliveResponse , Client ) async throws -> Void ) {
108- onConnectionKeepAlive = callback
109- }
110-
111- /// Define the callback run on receipt of a `data` message
112- /// - Parameter callback: The callback to assign
113- public func onData( _ callback: @escaping ( DataResponse , Client ) async throws -> Void ) {
114- onData = callback
115- }
116-
117- /// Define the callback run on receipt of an `error` message
118- /// - Parameter callback: The callback to assign
119- public func onError( _ callback: @escaping ( ErrorResponse , Client ) async throws -> Void ) {
120- onError = callback
121- }
122-
123- /// Define the callback run on receipt of any message
124- /// - Parameter callback: The callback to assign
125- public func onComplete( _ callback: @escaping ( CompleteResponse , Client ) async throws -> Void ) {
126- onComplete = callback
127- }
128-
129- /// Define the callback run on receipt of a `complete` message
130- /// - Parameter callback: The callback to assign
131- public func onMessage( _ callback: @escaping ( String , Client ) async throws -> Void ) {
132- onMessage = callback
133- }
134-
135112 /// Send a `connection_init` request through the messenger
136113 public func sendConnectionInit( payload: InitPayload ) async throws {
137- guard let messenger = messenger else { return }
138114 try await messenger. send (
139115 ConnectionInitRequest (
140116 payload: payload
@@ -144,7 +120,6 @@ public class Client<InitPayload: Equatable & Codable> {
144120
145121 /// Send a `start` request through the messenger
146122 public func sendStart( payload: GraphQLRequest , id: String ) async throws {
147- guard let messenger = messenger else { return }
148123 try await messenger. send (
149124 StartRequest (
150125 payload: payload,
@@ -155,7 +130,6 @@ public class Client<InitPayload: Equatable & Codable> {
155130
156131 /// Send a `stop` request through the messenger
157132 public func sendStop( id: String ) async throws {
158- guard let messenger = messenger else { return }
159133 try await messenger. send (
160134 StopRequest (
161135 id: id
@@ -165,15 +139,13 @@ public class Client<InitPayload: Equatable & Codable> {
165139
166140 /// Send a `connection_terminate` request through the messenger
167141 public func sendConnectionTerminate( ) async throws {
168- guard let messenger = messenger else { return }
169142 try await messenger. send (
170143 ConnectionTerminateRequest ( ) . toJSON ( encoder)
171144 )
172145 }
173146
174147 /// Send an error through the messenger and close the connection
175148 private func error( _ error: GraphQLWSError ) async throws {
176- guard let messenger = messenger else { return }
177149 try await messenger. error ( error. message, code: error. code. rawValue)
178150 }
179151}
0 commit comments