Skip to content

Commit 4a4ea22

Browse files
authored
Merge pull request #78 from NeedleInAJayStack/feature/SwiftConcurrency
Swift Concurrency Support
2 parents 7dfde3f + 8e69a74 commit 4a4ea22

File tree

8 files changed

+580
-11
lines changed

8 files changed

+580
-11
lines changed

Package.resolved

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

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ let package = Package(
77
.library(name: "Graphiti", targets: ["Graphiti"]),
88
],
99
dependencies: [
10-
.package(url: "https://github.com/GraphQLSwift/GraphQL.git", .upToNextMajor(from: "2.0.0"))
10+
.package(url: "https://github.com/GraphQLSwift/GraphQL.git", from: "2.4.0")
1111
],
1212
targets: [
1313
.target(name: "Graphiti", dependencies: ["GraphQL"]),

Sources/Graphiti/API/API.swift

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,46 @@ extension API {
4343
)
4444
}
4545
}
46+
47+
48+
#if compiler(>=5.5) && canImport(_Concurrency)
49+
50+
extension API {
51+
@available(macOS 12, iOS 15, watchOS 8, tvOS 15, *)
52+
public func execute(
53+
request: String,
54+
context: ContextType,
55+
on eventLoopGroup: EventLoopGroup,
56+
variables: [String: Map] = [:],
57+
operationName: String? = nil
58+
) async throws -> GraphQLResult {
59+
return try await schema.execute(
60+
request: request,
61+
resolver: resolver,
62+
context: context,
63+
eventLoopGroup: eventLoopGroup,
64+
variables: variables,
65+
operationName: operationName
66+
).get()
67+
}
68+
69+
@available(macOS 12, iOS 15, watchOS 8, tvOS 15, *)
70+
public func subscribe(
71+
request: String,
72+
context: ContextType,
73+
on eventLoopGroup: EventLoopGroup,
74+
variables: [String: Map] = [:],
75+
operationName: String? = nil
76+
) async throws -> SubscriptionResult {
77+
return try await schema.subscribe(
78+
request: request,
79+
resolver: resolver,
80+
context: context,
81+
eventLoopGroup: eventLoopGroup,
82+
variables: variables,
83+
operationName: operationName
84+
).get()
85+
}
86+
}
87+
88+
#endif

Sources/Graphiti/Field/Field/Field.swift

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,3 +247,72 @@ public extension Field where Arguments == NoArguments {
247247
self.init(name: name, arguments: [], syncResolve: syncResolve)
248248
}
249249
}
250+
251+
#if compiler(>=5.5) && canImport(_Concurrency)
252+
253+
public extension Field {
254+
255+
@available(macOS 12, iOS 15, watchOS 8, tvOS 15, *)
256+
convenience init<ResolveType>(
257+
name: String,
258+
arguments: [ArgumentComponent<Arguments>],
259+
concurrentResolve: @escaping ConcurrentResolve<ObjectType, Context, Arguments, ResolveType>
260+
) {
261+
let asyncResolve: AsyncResolve<ObjectType, Context, Arguments, ResolveType> = { type in
262+
{ context, arguments, eventLoopGroup in
263+
let promise = eventLoopGroup.next().makePromise(of: ResolveType.self)
264+
promise.completeWithTask {
265+
try await concurrentResolve(type)(context, arguments)
266+
}
267+
return promise.futureResult
268+
}
269+
}
270+
self.init(name: name, arguments: arguments, asyncResolve: asyncResolve)
271+
}
272+
}
273+
274+
// MARK: ConcurrentResolve Initializers
275+
276+
public extension Field where FieldType : Encodable {
277+
@available(macOS 12, iOS 15, watchOS 8, tvOS 15, *)
278+
convenience init(
279+
_ name: String,
280+
at function: @escaping ConcurrentResolve<ObjectType, Context, Arguments, FieldType>,
281+
@ArgumentComponentBuilder<Arguments> _ argument: () -> ArgumentComponent<Arguments>
282+
) {
283+
self.init(name: name, arguments: [argument()], concurrentResolve: function)
284+
}
285+
286+
@available(macOS 12, iOS 15, watchOS 8, tvOS 15, *)
287+
convenience init(
288+
_ name: String,
289+
at function: @escaping ConcurrentResolve<ObjectType, Context, Arguments, FieldType>,
290+
@ArgumentComponentBuilder<Arguments> _ arguments: () -> [ArgumentComponent<Arguments>] = {[]}
291+
) {
292+
self.init(name: name, arguments: arguments(), concurrentResolve: function)
293+
}
294+
}
295+
296+
public extension Field {
297+
@available(macOS 12, iOS 15, watchOS 8, tvOS 15, *)
298+
convenience init<ResolveType>(
299+
_ name: String,
300+
at function: @escaping ConcurrentResolve<ObjectType, Context, Arguments, ResolveType>,
301+
as: FieldType.Type,
302+
@ArgumentComponentBuilder<Arguments> _ argument: () -> ArgumentComponent<Arguments>
303+
) {
304+
self.init(name: name, arguments: [argument()], concurrentResolve: function)
305+
}
306+
307+
@available(macOS 12, iOS 15, watchOS 8, tvOS 15, *)
308+
convenience init<ResolveType>(
309+
_ name: String,
310+
at function: @escaping ConcurrentResolve<ObjectType, Context, Arguments, ResolveType>,
311+
as: FieldType.Type,
312+
@ArgumentComponentBuilder<Arguments> _ arguments: () -> [ArgumentComponent<Arguments>] = {[]}
313+
) {
314+
self.init(name: name, arguments: arguments(), concurrentResolve: function)
315+
}
316+
}
317+
318+
#endif
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import NIO
2+
3+
#if compiler(>=5.5) && canImport(_Concurrency)
4+
5+
@available(macOS 12, iOS 15, watchOS 8, tvOS 15, *)
6+
public typealias ConcurrentResolve<ObjectType, Context, Arguments, ResolveType> = (
7+
_ object: ObjectType
8+
) -> (
9+
_ context: Context,
10+
_ arguments: Arguments
11+
) async throws -> ResolveType
12+
13+
#endif

Sources/Graphiti/Subscription/SubscribeField.swift

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,109 @@ public extension SubscriptionField {
357357
}
358358
}
359359

360+
#if compiler(>=5.5) && canImport(_Concurrency)
361+
362+
public extension SubscriptionField {
363+
364+
@available(macOS 12, iOS 15, watchOS 8, tvOS 15, *)
365+
convenience init<ResolveType>(
366+
name: String,
367+
arguments: [ArgumentComponent<Arguments>],
368+
concurrentResolve: @escaping ConcurrentResolve<SourceEventType, Context, Arguments, ResolveType>,
369+
concurrentSubscribe: @escaping ConcurrentResolve<ObjectType, Context, Arguments, EventStream<SourceEventType>>
370+
) {
371+
let asyncResolve: AsyncResolve<SourceEventType, Context, Arguments, ResolveType> = { type in
372+
{ context, arguments, eventLoopGroup in
373+
let promise = eventLoopGroup.next().makePromise(of: ResolveType.self)
374+
promise.completeWithTask {
375+
try await concurrentResolve(type)(context, arguments)
376+
}
377+
return promise.futureResult
378+
}
379+
}
380+
let asyncSubscribe: AsyncResolve<ObjectType, Context, Arguments, EventStream<SourceEventType>> = { type in
381+
{ context, arguments, eventLoopGroup in
382+
let promise = eventLoopGroup.next().makePromise(of: EventStream<SourceEventType>.self)
383+
promise.completeWithTask {
384+
try await concurrentSubscribe(type)(context, arguments)
385+
}
386+
return promise.futureResult
387+
}
388+
}
389+
self.init(name: name, arguments: arguments, asyncResolve: asyncResolve, asyncSubscribe: asyncSubscribe)
390+
}
391+
392+
@available(macOS 12, iOS 15, watchOS 8, tvOS 15, *)
393+
convenience init(
394+
name: String,
395+
arguments: [ArgumentComponent<Arguments>],
396+
as: FieldType.Type,
397+
concurrentSubscribe: @escaping ConcurrentResolve<ObjectType, Context, Arguments, EventStream<SourceEventType>>
398+
) {
399+
let asyncSubscribe: AsyncResolve<ObjectType, Context, Arguments, EventStream<SourceEventType>> = { type in
400+
{ context, arguments, eventLoopGroup in
401+
let promise = eventLoopGroup.next().makePromise(of: EventStream<SourceEventType>.self)
402+
promise.completeWithTask {
403+
try await concurrentSubscribe(type)(context, arguments)
404+
}
405+
return promise.futureResult
406+
}
407+
}
408+
self.init(name: name, arguments: arguments, as: `as`, asyncSubscribe: asyncSubscribe)
409+
}
410+
}
411+
412+
// MARK: ConcurrentResolve Initializers
413+
414+
public extension SubscriptionField where FieldType : Encodable {
415+
@available(macOS 12, iOS 15, watchOS 8, tvOS 15, *)
416+
convenience init(
417+
_ name: String,
418+
at function: @escaping ConcurrentResolve<SourceEventType, Context, Arguments, FieldType>,
419+
atSub subFunc: @escaping ConcurrentResolve<ObjectType, Context, Arguments, EventStream<SourceEventType>>,
420+
@ArgumentComponentBuilder<Arguments> _ argument: () -> ArgumentComponent<Arguments>
421+
) {
422+
self.init(name: name, arguments: [argument()], concurrentResolve: function, concurrentSubscribe: subFunc)
423+
}
424+
425+
@available(macOS 12, iOS 15, watchOS 8, tvOS 15, *)
426+
convenience init(
427+
_ name: String,
428+
at function: @escaping ConcurrentResolve<SourceEventType, Context, Arguments, FieldType>,
429+
atSub subFunc: @escaping ConcurrentResolve<ObjectType, Context, Arguments, EventStream<SourceEventType>>,
430+
@ArgumentComponentBuilder<Arguments> _ arguments: () -> [ArgumentComponent<Arguments>] = {[]}
431+
) {
432+
self.init(name: name, arguments: arguments(), concurrentResolve: function, concurrentSubscribe: subFunc)
433+
}
434+
}
435+
436+
public extension SubscriptionField {
437+
@available(macOS 12, iOS 15, watchOS 8, tvOS 15, *)
438+
convenience init<ResolveType>(
439+
_ name: String,
440+
at function: @escaping ConcurrentResolve<SourceEventType, Context, Arguments, ResolveType>,
441+
as: FieldType.Type,
442+
atSub subFunc: @escaping ConcurrentResolve<ObjectType, Context, Arguments, EventStream<SourceEventType>>,
443+
@ArgumentComponentBuilder<Arguments> _ argument: () -> ArgumentComponent<Arguments>
444+
) {
445+
self.init(name: name, arguments: [argument()], concurrentResolve: function, concurrentSubscribe: subFunc)
446+
}
447+
448+
@available(macOS 12, iOS 15, watchOS 8, tvOS 15, *)
449+
convenience init<ResolveType>(
450+
_ name: String,
451+
at function: @escaping ConcurrentResolve<SourceEventType, Context, Arguments, ResolveType>,
452+
as: FieldType.Type,
453+
atSub subFunc: @escaping ConcurrentResolve<ObjectType, Context, Arguments, EventStream<SourceEventType>>,
454+
@ArgumentComponentBuilder<Arguments> _ arguments: () -> [ArgumentComponent<Arguments>] = {[]}
455+
) {
456+
self.init(name: name, arguments: arguments(), concurrentResolve: function, concurrentSubscribe: subFunc)
457+
}
458+
}
459+
460+
#endif
461+
462+
360463
// TODO Determine if we can use keypaths to initialize
361464

362465
// MARK: Keypath Initializers

0 commit comments

Comments
 (0)