Skip to content

Commit 7332888

Browse files
feat: Converts to Swift Concurrency
This adopts GraphQL v4, Graphiti v3, and removes the RxSwift and NIO dependencies.
1 parent 5efdea7 commit 7332888

File tree

14 files changed

+471
-507
lines changed

14 files changed

+471
-507
lines changed

Package.resolved

Lines changed: 6 additions & 33 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,25 @@ import PackageDescription
44

55
let package = Package(
66
name: "GraphQLTransportWS",
7+
platforms: [.macOS(.v10_15)],
78
products: [
89
.library(
910
name: "GraphQLTransportWS",
1011
targets: ["GraphQLTransportWS"]
1112
),
1213
],
1314
dependencies: [
14-
.package(name: "Graphiti", url: "https://github.com/GraphQLSwift/Graphiti.git", from: "1.0.0"),
15-
.package(name: "GraphQL", url: "https://github.com/GraphQLSwift/GraphQL.git", from: "2.2.1"),
16-
.package(name: "GraphQLRxSwift", url: "https://github.com/GraphQLSwift/GraphQLRxSwift.git", from: "0.0.4"),
17-
.package(name: "RxSwift", url: "https://github.com/ReactiveX/RxSwift.git", from: "6.1.0"),
18-
.package(name: "swift-nio", url: "https://github.com/apple/swift-nio.git", from: "2.33.0"),
15+
.package(url: "https://github.com/GraphQLSwift/Graphiti.git", from: "3.0.0"),
16+
.package(url: "https://github.com/GraphQLSwift/GraphQL.git", from: "4.0.0"),
1917
],
2018
targets: [
2119
.target(
2220
name: "GraphQLTransportWS",
2321
dependencies: [
2422
.product(name: "Graphiti", package: "Graphiti"),
25-
.product(name: "GraphQLRxSwift", package: "GraphQLRxSwift"),
2623
.product(name: "GraphQL", package: "GraphQL"),
27-
.product(name: "NIO", package: "swift-nio"),
28-
.product(name: "RxSwift", package: "RxSwift")
29-
]),
24+
]
25+
),
3026
.testTarget(
3127
name: "GraphQLTransportWSTests",
3228
dependencies: ["GraphQLTransportWS"]

README.md

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -27,32 +27,32 @@ import GraphQLTransportWS
2727
/// Messenger wrapper for WebSockets
2828
class WebSocketMessenger: Messenger {
2929
private weak var websocket: WebSocket?
30-
private var onReceive: (String) -> Void = { _ in }
31-
30+
private var onReceive: (String) async throws -> Void = { _ in }
31+
3232
init(websocket: WebSocket) {
3333
self.websocket = websocket
3434
websocket.onText { _, message in
35-
self.onReceive(message)
35+
try await self.onReceive(message)
3636
}
3737
}
38-
39-
func send<S>(_ message: S) where S: Collection, S.Element == Character {
38+
39+
func send<S>(_ message: S) where S: Collection, S.Element == Character async throws {
4040
guard let websocket = websocket else { return }
41-
websocket.send(message)
41+
try await websocket.send(message)
4242
}
43-
44-
func onReceive(callback: @escaping (String) -> Void) {
43+
44+
func onReceive(callback: @escaping (String) async throws -> Void) {
4545
self.onReceive = callback
4646
}
47-
48-
func error(_ message: String, code: Int) {
47+
48+
func error(_ message: String, code: Int) async throws {
4949
guard let websocket = websocket else { return }
50-
websocket.send("\(code): \(message)")
50+
try await websocket.send("\(code): \(message)")
5151
}
52-
53-
func close() {
52+
53+
func close() async throws {
5454
guard let websocket = websocket else { return }
55-
_ = websocket.close()
55+
try await websocket.close()
5656
}
5757
}
5858
```
@@ -67,7 +67,7 @@ routes.webSocket(
6767
let server = GraphQLTransportWS.Server<EmptyInitPayload?>(
6868
messenger: messenger,
6969
onExecute: { graphQLRequest in
70-
api.execute(
70+
try await api.execute(
7171
request: graphQLRequest.query,
7272
context: context,
7373
on: self.eventLoop,
@@ -76,7 +76,7 @@ routes.webSocket(
7676
)
7777
},
7878
onSubscribe: { graphQLRequest in
79-
api.subscribe(
79+
try await api.subscribe(
8080
request: graphQLRequest.query,
8181
context: context,
8282
on: self.eventLoop,
@@ -128,8 +128,8 @@ If the `payload` field is not required on your server, you may make Server's gen
128128

129129
## Memory Management
130130

131-
Memory ownership among the Server, Client, and Messenger may seem a little backwards. This is because the Swift/Vapor WebSocket
132-
implementation persists WebSocket objects long after their callback and they are expected to retain strong memory references to the
131+
Memory ownership among the Server, Client, and Messenger may seem a little backwards. This is because the Swift/Vapor WebSocket
132+
implementation persists WebSocket objects long after their callback and they are expected to retain strong memory references to the
133133
objects required for responses. In order to align cleanly and avoid memory cycles, Server and Client are injected strongly into Messenger
134134
callbacks, and only hold weak references to their Messenger. This means that Messenger objects (or their enclosing WebSocket) must
135135
be persisted to have the connected Server or Client objects function. That is, if a Server's Messenger falls out of scope and deinitializes,

Sources/GraphQLTransportWS/Client.swift

Lines changed: 69 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,16 @@ import GraphQL
55
public class Client<InitPayload: Equatable & Codable> {
66
// We keep this weak because we strongly inject this object into the messenger callback
77
weak var messenger: Messenger?
8-
9-
var onConnectionAck: (ConnectionAckResponse, Client) -> Void = { _, _ in }
10-
var onNext: (NextResponse, Client) -> Void = { _, _ in }
11-
var onError: (ErrorResponse, Client) -> Void = { _, _ in }
12-
var onComplete: (CompleteResponse, Client) -> Void = { _, _ in }
13-
var onMessage: (String, Client) -> Void = { _, _ in }
14-
8+
9+
var onConnectionAck: (ConnectionAckResponse, Client) async throws -> Void = { _, _ in }
10+
var onNext: (NextResponse, Client) async throws -> Void = { _, _ in }
11+
var onError: (ErrorResponse, Client) async throws -> Void = { _, _ in }
12+
var onComplete: (CompleteResponse, Client) async throws -> Void = { _, _ in }
13+
var onMessage: (String, Client) async throws -> Void = { _, _ in }
14+
1515
let encoder = GraphQLJSONEncoder()
1616
let decoder = JSONDecoder()
17-
17+
1818
/// Create a new client.
1919
///
2020
/// - Parameters:
@@ -24,123 +24,122 @@ public class Client<InitPayload: Equatable & Codable> {
2424
) {
2525
self.messenger = messenger
2626
messenger.onReceive { message in
27-
self.onMessage(message, self)
28-
27+
try await self.onMessage(message, self)
28+
2929
// Detect and ignore error responses.
3030
if message.starts(with: "44") {
3131
// TODO: Determine what to do with returned error messages
3232
return
3333
}
34-
34+
3535
guard let json = message.data(using: .utf8) else {
36-
self.error(.invalidEncoding())
36+
try await self.error(.invalidEncoding())
3737
return
3838
}
39-
39+
4040
let response: Response
4141
do {
4242
response = try self.decoder.decode(Response.self, from: json)
43-
}
44-
catch {
45-
self.error(.noType())
43+
} catch {
44+
try await self.error(.noType())
4645
return
4746
}
48-
47+
4948
switch response.type {
50-
case .connectionAck:
51-
guard let connectionAckResponse = try? self.decoder.decode(ConnectionAckResponse.self, from: json) else {
52-
self.error(.invalidResponseFormat(messageType: .connectionAck))
53-
return
54-
}
55-
self.onConnectionAck(connectionAckResponse, self)
56-
case .next:
57-
guard let nextResponse = try? self.decoder.decode(NextResponse.self, from: json) else {
58-
self.error(.invalidResponseFormat(messageType: .next))
59-
return
60-
}
61-
self.onNext(nextResponse, self)
62-
case .error:
63-
guard let errorResponse = try? self.decoder.decode(ErrorResponse.self, from: json) else {
64-
self.error(.invalidResponseFormat(messageType: .error))
65-
return
66-
}
67-
self.onError(errorResponse, self)
68-
case .complete:
69-
guard let completeResponse = try? self.decoder.decode(CompleteResponse.self, from: json) else {
70-
self.error(.invalidResponseFormat(messageType: .complete))
71-
return
72-
}
73-
self.onComplete(completeResponse, self)
74-
case .unknown:
75-
self.error(.invalidType())
49+
case .connectionAck:
50+
guard let connectionAckResponse = try? self.decoder.decode(ConnectionAckResponse.self, from: json) else {
51+
try await self.error(.invalidResponseFormat(messageType: .connectionAck))
52+
return
53+
}
54+
try await self.onConnectionAck(connectionAckResponse, self)
55+
case .next:
56+
guard let nextResponse = try? self.decoder.decode(NextResponse.self, from: json) else {
57+
try await self.error(.invalidResponseFormat(messageType: .next))
58+
return
59+
}
60+
try await self.onNext(nextResponse, self)
61+
case .error:
62+
guard let errorResponse = try? self.decoder.decode(ErrorResponse.self, from: json) else {
63+
try await self.error(.invalidResponseFormat(messageType: .error))
64+
return
65+
}
66+
try await self.onError(errorResponse, self)
67+
case .complete:
68+
guard let completeResponse = try? self.decoder.decode(CompleteResponse.self, from: json) else {
69+
try await self.error(.invalidResponseFormat(messageType: .complete))
70+
return
71+
}
72+
try await self.onComplete(completeResponse, self)
73+
case .unknown:
74+
try await self.error(.invalidType())
7675
}
7776
}
7877
}
79-
78+
8079
/// Define the callback run on receipt of a `connection_ack` message
8180
/// - Parameter callback: The callback to assign
82-
public func onConnectionAck(_ callback: @escaping (ConnectionAckResponse, Client) -> Void) {
83-
self.onConnectionAck = callback
81+
public func onConnectionAck(_ callback: @escaping (ConnectionAckResponse, Client) async throws -> Void) {
82+
onConnectionAck = callback
8483
}
85-
84+
8685
/// Define the callback run on receipt of a `next` message
8786
/// - Parameter callback: The callback to assign
88-
public func onNext(_ callback: @escaping (NextResponse, Client) -> Void) {
89-
self.onNext = callback
87+
public func onNext(_ callback: @escaping (NextResponse, Client) async throws -> Void) {
88+
onNext = callback
9089
}
91-
90+
9291
/// Define the callback run on receipt of an `error` message
9392
/// - Parameter callback: The callback to assign
94-
public func onError(_ callback: @escaping (ErrorResponse, Client) -> Void) {
95-
self.onError = callback
93+
public func onError(_ callback: @escaping (ErrorResponse, Client) async throws -> Void) {
94+
onError = callback
9695
}
97-
96+
9897
/// Define the callback run on receipt of a `complete` message
9998
/// - Parameter callback: The callback to assign
100-
public func onComplete(_ callback: @escaping (CompleteResponse, Client) -> Void) {
101-
self.onComplete = callback
99+
public func onComplete(_ callback: @escaping (CompleteResponse, Client) async throws -> Void) {
100+
onComplete = callback
102101
}
103-
102+
104103
/// Define the callback run on receipt of any message
105104
/// - Parameter callback: The callback to assign
106-
public func onMessage(_ callback: @escaping (String, Client) -> Void) {
107-
self.onMessage = callback
105+
public func onMessage(_ callback: @escaping (String, Client) async throws -> Void) {
106+
onMessage = callback
108107
}
109-
108+
110109
/// Send a `connection_init` request through the messenger
111-
public func sendConnectionInit(payload: InitPayload) {
110+
public func sendConnectionInit(payload: InitPayload) async throws {
112111
guard let messenger = messenger else { return }
113-
messenger.send(
112+
try await messenger.send(
114113
ConnectionInitRequest(
115114
payload: payload
116115
).toJSON(encoder)
117116
)
118117
}
119-
118+
120119
/// Send a `subscribe` request through the messenger
121-
public func sendStart(payload: GraphQLRequest, id: String) {
120+
public func sendStart(payload: GraphQLRequest, id: String) async throws {
122121
guard let messenger = messenger else { return }
123-
messenger.send(
122+
try await messenger.send(
124123
SubscribeRequest(
125124
payload: payload,
126125
id: id
127126
).toJSON(encoder)
128127
)
129128
}
130-
129+
131130
/// Send a `complete` request through the messenger
132-
public func sendStop(id: String) {
131+
public func sendStop(id: String) async throws {
133132
guard let messenger = messenger else { return }
134-
messenger.send(
133+
try await messenger.send(
135134
CompleteRequest(
136135
id: id
137136
).toJSON(encoder)
138137
)
139138
}
140-
139+
141140
/// Send an error through the messenger and close the connection
142-
private func error(_ error: GraphQLTransportWSError) {
141+
private func error(_ error: GraphQLTransportWSError) async throws {
143142
guard let messenger = messenger else { return }
144-
messenger.error(error.message, code: error.code.rawValue)
143+
try await messenger.error(error.message, code: error.code.rawValue)
145144
}
146145
}

0 commit comments

Comments
 (0)