Skip to content

Commit eedec2b

Browse files
Merge pull request #166 from NeedleInAJayStack/feat/swift-concurrency
Breaking: Converts to Swift Concurrency
2 parents e3c5118 + 210fb72 commit eedec2b

File tree

60 files changed

+1819
-3488
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+1819
-3488
lines changed

MIGRATION.md

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,62 @@
11
# Migration
22

3-
## 2.0 to 3.0
3+
## 3 to 4
4+
5+
### NIO removal
6+
7+
All NIO-based arguments and return types were removed, including all `EventLoopGroup` and `EventLoopFuture` parameters.
8+
9+
As such, all `execute` and `subscribe` calls should have the `eventLoopGroup` argument removed, and the `await` keyword should be used.
10+
11+
Also, all resolver closures must remove the `eventLoopGroup` argument, and all that return an `EventLoopFuture` should be converted to an `async` function.
12+
13+
The documentation here will be very helpful in the conversion: https://www.swift.org/documentation/server/guides/libraries/concurrency-adoption-guidelines.html
14+
15+
### Swift Concurrency checking
16+
17+
With the conversion from NIO to Swift Concurrency, types used across async boundaries should conform to `Sendable` to avoid errors and warnings. This includes the Swift types and functions that back the GraphQL schema. For more details on the conversion, see the [Sendable documentation](https://developer.apple.com/documentation/swift/sendable).
18+
19+
### `ExecutionStrategy` argument removals
20+
21+
The `queryStrategy`, `mutationStrategy`, and `subscriptionStrategy` arguments have been removed from `graphql` and `graphqlSubscribe`. Instead Queries and Subscriptions are executed in parallel and Mutations are executed serially, [as required by the spec](https://spec.graphql.org/October2021/#sec-Mutation).
22+
23+
### `validationRules` argument reorder
24+
25+
The `validationRules` argument has been moved from the beginning of `graphql` and `graphqlSubscribe` to the end to better reflect its relative importance:
26+
27+
28+
```swift
29+
// Before
30+
let result = try await graphql(
31+
validationRules: [ruleABC],
32+
schema: schema,
33+
...
34+
)
35+
// After
36+
let result = try await graphql(
37+
schema: schema,
38+
...
39+
validationRules: [ruleABC]
40+
)
41+
```
42+
43+
### EventStream removal
44+
45+
The `EventStream` abstraction used to provide pre-concurrency subscription support has been removed. This means that `graphqlSubscribe(...).stream` will now be an `AsyncThrowingStream<GraphQLResult, Error>` type, instead of an `EventStream` type, and that downcasting to `ConcurrentEventStream` is no longer necessary.
46+
47+
### SubscriptionResult removal
48+
49+
The `SubscriptionResult` type was removed, and `graphqlSubscribe` now returns `Result<AsyncThrowingStream<GraphQLResult, Error>, GraphQLErrors>`.
50+
51+
### Instrumentation removal
52+
53+
The `Instrumentation` type has been removed, with anticipated support for tracing using [`swift-distributed-tracing`](https://github.com/apple/swift-distributed-tracing). `instrumentation` arguments must be removed from `graphql` and `graphqlSubscribe` calls.
54+
55+
### AST Node `set`
56+
57+
The deprecated `Node.set(value: Node?, key: String)` function was removed in preference of the `Node.set(value _: NodeResult?, key _: String)`. Change any calls from `node.set(value: node, key: string)` to `node.set(.node(node), string)`.
58+
59+
## 2 to 3
460

561
### TypeReference removal
662

@@ -73,4 +129,4 @@ The following type properties were changed from arrays to closures. To get the a
73129

74130
### GraphQL type codability
75131

76-
With GraphQL type definitions now including closures, many of the objects in [Definition](https://github.com/GraphQLSwift/GraphQL/blob/main/Sources/GraphQL/Type/Definition.swift) are no longer codable. If you are depending on codability, you can conform the type appropriately in your downstream package.
132+
With GraphQL type definitions now including closures, many of the objects in [Definition](https://github.com/GraphQLSwift/GraphQL/blob/main/Sources/GraphQL/Type/Definition.swift) are no longer codable. If you are depending on codability, you can conform the type appropriately in your downstream package.

Package.resolved

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

Package.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,17 @@ import PackageDescription
33

44
let package = Package(
55
name: "GraphQL",
6+
platforms: [.macOS(.v10_15), .iOS(.v13), .tvOS(.v13), .watchOS(.v6)],
67
products: [
78
.library(name: "GraphQL", targets: ["GraphQL"]),
89
],
910
dependencies: [
10-
.package(url: "https://github.com/apple/swift-nio.git", .upToNextMajor(from: "2.10.1")),
1111
.package(url: "https://github.com/apple/swift-collections", .upToNextMajor(from: "1.0.0")),
1212
],
1313
targets: [
1414
.target(
1515
name: "GraphQL",
1616
dependencies: [
17-
.product(name: "NIO", package: "swift-nio"),
1817
.product(name: "OrderedCollections", package: "swift-collections"),
1918
]
2019
),
@@ -26,5 +25,6 @@ let package = Package(
2625
.copy("LanguageTests/schema-kitchen-sink.graphql"),
2726
]
2827
),
29-
]
28+
],
29+
swiftLanguageVersions: [.v5, .version("6")]
3030
)

README.md

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,7 @@ Once a schema has been defined queries may be executed against it using the glob
4545
```swift
4646
let result = try await graphql(
4747
schema: schema,
48-
request: "{ hello }",
49-
eventLoopGroup: eventLoopGroup
48+
request: "{ hello }"
5049
)
5150
```
5251

@@ -58,33 +57,29 @@ The result of this query is a `GraphQLResult` that encodes to the following JSON
5857

5958
### Subscription
6059

61-
This package supports GraphQL subscription, but until the integration of `AsyncSequence` in Swift 5.5 the standard Swift library did not
62-
provide an event-stream construct. For historical reasons and backwards compatibility, this library implements subscriptions using an
63-
`EventStream` protocol that nearly every asynchronous stream implementation can conform to.
64-
65-
To create a subscription field in a GraphQL schema, use the `subscribe` resolver that returns an `EventStream`. You must also provide a
66-
`resolver`, which defines how to process each event as it occurs and must return the field result type. Here is an example:
60+
This package supports GraphQL subscription. To create a subscription field in a GraphQL schema, use the `subscribe`
61+
resolver that returns any type that conforms to `AsyncSequence`. You must also provide a `resolver`, which defines how
62+
to process each event as it occurs and must return the field result type. Here is an example:
6763

6864
```swift
6965
let schema = try GraphQLSchema(
7066
subscribe: GraphQLObjectType(
7167
name: "Subscribe",
7268
fields: [
73-
"hello": GraphQLField(
69+
"hello": GraphQLField(
7470
type: GraphQLString,
75-
resolve: { eventResult, _, _, _, _ in // Defines how to transform each event when it occurs
71+
resolve: { eventResult, _, _, _ in // Defines how to transform each event when it occurs
7672
return eventResult
7773
},
78-
subscribe: { _, _, _, _, _ in // Defines how to construct the event stream
79-
let asyncStream = AsyncThrowingStream<String, Error> { continuation in
74+
subscribe: { _, _, _, _ in // Defines how to construct the event stream
75+
return AsyncThrowingStream<String, Error> { continuation in
8076
let timer = Timer.scheduledTimer(
8177
withTimeInterval: 3,
8278
repeats: true,
8379
) {
84-
continuation.yield("world") // Emits "world" every 3 seconds
80+
continuation.yield("world") // Emits "world" every 3 seconds
8581
}
8682
}
87-
return ConcurrentEventStream<String>(asyncStream)
8883
}
8984
)
9085
]
@@ -98,9 +93,8 @@ To execute a subscription use the `graphqlSubscribe` function:
9893
let subscriptionResult = try await graphqlSubscribe(
9994
schema: schema,
10095
)
101-
// Must downcast from EventStream to concrete type to use in 'for await' loop below
102-
let concurrentStream = subscriptionResult.stream! as! ConcurrentEventStream
103-
for try await result in concurrentStream.stream {
96+
let stream = subscriptionResult.get()
97+
for try await result in stream {
10498
print(result)
10599
}
106100
```
@@ -111,18 +105,15 @@ The code above will print the following JSON every 3 seconds:
111105
{ "hello": "world" }
112106
```
113107

114-
The example above assumes that your environment has access to Swift Concurrency. If that is not the case, try using
115-
[GraphQLRxSwift](https://github.com/GraphQLSwift/GraphQLRxSwift)
116-
117108
## Encoding Results
118109

119-
If you encode a `GraphQLResult` with an ordinary `JSONEncoder`, there are no guarantees that the field order will match the query,
110+
If you encode a `GraphQLResult` with an ordinary `JSONEncoder`, there are no guarantees that the field order will match the query,
120111
violating the [GraphQL spec](https://spec.graphql.org/June2018/#sec-Serialized-Map-Ordering). To preserve this order, `GraphQLResult`
121112
should be encoded using the `GraphQLJSONEncoder` provided by this package.
122113

123114
## Support
124115

125-
This package supports Swift versions in [alignment with Swift NIO](https://github.com/apple/swift-nio?tab=readme-ov-file#swift-versions).
116+
This package aims to support the previous three Swift versions.
126117

127118
For details on upgrading to new major versions, see [MIGRATION](MIGRATION.md).
128119

@@ -140,7 +131,7 @@ To format your code, install `swiftformat` and run:
140131

141132
```bash
142133
swiftformat .
143-
```
134+
```
144135

145136
Most of this repo mirrors the structure of
146137
(the canonical GraphQL implementation written in Javascript/Typescript)[https://github.com/graphql/graphql-js]. If there is any feature

Sources/GraphQL/Error/GraphQLError.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ extension GraphQLError: Hashable {
169169

170170
// MARK: IndexPath
171171

172-
public struct IndexPath: Codable {
172+
public struct IndexPath: Codable, Sendable {
173173
public let elements: [IndexPathValue]
174174

175175
public init(_ elements: [IndexPathElement] = []) {
@@ -197,7 +197,7 @@ extension IndexPath: ExpressibleByArrayLiteral {
197197
}
198198
}
199199

200-
public enum IndexPathValue: Codable, Equatable {
200+
public enum IndexPathValue: Codable, Equatable, Sendable {
201201
case index(Int)
202202
case key(String)
203203

0 commit comments

Comments
 (0)