Skip to content

Commit cacdd36

Browse files
committed
Seperate transport functions by operation type
This allows for generic dispatch based on operation type to work properly.
1 parent 7d09ffb commit cacdd36

File tree

6 files changed

+138
-58
lines changed

6 files changed

+138
-58
lines changed

apollo-ios/Sources/Apollo/ApolloClient.swift

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import ApolloAPI
55
#endif
66

77
/// A cache policy that specifies whether results should be fetched from the server or loaded from the local cache.
8+
#warning("TODO: rethink this API. Onlye valid for queries currently")
89
public enum CachePolicy: Sendable, Hashable {
910
/// Return data from the cache if available, else fetch results from the server.
1011
case returnCacheDataElseFetch
@@ -39,11 +40,14 @@ public class ApolloClient: ApolloClientProtocol, @unchecked Sendable {
3940

4041
public enum ApolloClientError: Error, LocalizedError, Hashable {
4142
case noUploadTransport
43+
case noSubscriptionTransport
4244

4345
public var errorDescription: String? {
4446
switch self {
4547
case .noUploadTransport:
4648
return "Attempting to upload using a transport which does not support uploads. This is a developer error."
49+
case .noSubscriptionTransport:
50+
return "Attempting to begin a subscription using a transport which does not support subscriptions. This is a developer error."
4751
}
4852
}
4953
}
@@ -103,8 +107,8 @@ public class ApolloClient: ApolloClientProtocol, @unchecked Sendable {
103107
) -> (any Cancellable) {
104108
return awaitStreamInTask(
105109
{
106-
try await self.networkTransport.send(
107-
operation: query,
110+
try self.networkTransport.send(
111+
query: query,
108112
cachePolicy: cachePolicy,
109113
contextIdentifier: contextIdentifier,
110114
context: context
@@ -185,7 +189,7 @@ public class ApolloClient: ApolloClientProtocol, @unchecked Sendable {
185189
return awaitStreamInTask(
186190
{
187191
try await self.networkTransport.send(
188-
operation: mutation,
192+
mutation: mutation,
189193
cachePolicy: publishResultToStore ? .default : .fetchIgnoringCacheCompletely,
190194
contextIdentifier: contextIdentifier,
191195
context: context
@@ -214,7 +218,7 @@ public class ApolloClient: ApolloClientProtocol, @unchecked Sendable {
214218

215219
return awaitStreamInTask(
216220
{
217-
try await uploadingTransport.upload(
221+
try uploadingTransport.upload(
218222
operation: operation,
219223
files: files,
220224
context: context
@@ -231,10 +235,18 @@ public class ApolloClient: ApolloClientProtocol, @unchecked Sendable {
231235
queue: DispatchQueue = .main,
232236
resultHandler: @escaping GraphQLResultHandler<Subscription.Data>
233237
) -> any Cancellable {
238+
guard let networkTransport = networkTransport as? (any SubscriptionNetworkTransport) else {
239+
assertionFailure("Trying to subscribe without a subscription transport. Please make sure your network transport conforms to `SubscriptionNetworkTransport`.")
240+
queue.async {
241+
resultHandler(.failure(ApolloClientError.noSubscriptionTransport))
242+
}
243+
return EmptyCancellable()
244+
}
245+
234246
return awaitStreamInTask(
235247
{
236-
try await self.networkTransport.send(
237-
operation: subscription,
248+
try networkTransport.send(
249+
subscription: subscription,
238250
cachePolicy: .default,
239251
contextIdentifier: nil,
240252
context: context)

apollo-ios/Sources/Apollo/Interceptors/JSONResponseParsingInterceptor.swift

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,6 @@ public struct JSONResponseParsingInterceptor: ApolloInterceptor {
2424

2525
let currentResult = CurrentResult<Request.Operation>()
2626

27-
if let request = request as? UploadRequest<Request.Operation> {
28-
29-
}
30-
3127
return try await next(request).compactMap { result -> InterceptorResult<Request.Operation>? in
3228
let parser = JSONResponseParser<Request.Operation>(
3329
response: result.response,

apollo-ios/Sources/Apollo/NetworkTransport.swift

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,32 @@ public protocol NetworkTransport: AnyObject, Sendable {
1616
/// - contextIdentifier: [optional] A unique identifier for this request, to help with deduping cache hits for watchers. Defaults to `nil`.
1717
/// - context: [optional] A context that is being passed through the request chain. Defaults to `nil`.
1818
/// - Returns: A stream of `GraphQLResult`s for each response.
19-
func send<Operation: GraphQLOperation>(
20-
operation: Operation,
19+
func send<Query: GraphQLQuery>(
20+
query: Query,
2121
cachePolicy: CachePolicy,
2222
contextIdentifier: UUID?,
2323
context: (any RequestContext)?
24-
) throws -> AsyncThrowingStream<GraphQLResult<Operation.Data>, any Error>
24+
) throws -> AsyncThrowingStream<GraphQLResult<Query.Data>, any Error>
25+
26+
func send<Mutation: GraphQLMutation>(
27+
mutation: Mutation,
28+
cachePolicy: CachePolicy,
29+
contextIdentifier: UUID?,
30+
context: (any RequestContext)?
31+
) throws -> AsyncThrowingStream<GraphQLResult<Mutation.Data>, any Error>
32+
33+
}
34+
35+
// MARK: -
36+
37+
public protocol SubscriptionNetworkTransport: NetworkTransport {
38+
39+
func send<Subscription: GraphQLSubscription>(
40+
subscription: Subscription,
41+
cachePolicy: CachePolicy,
42+
contextIdentifier: UUID?,
43+
context: (any RequestContext)?
44+
) throws -> AsyncThrowingStream<GraphQLResult<Subscription.Data>, any Error>
2545

2646
}
2747

@@ -37,9 +57,10 @@ public protocol UploadingNetworkTransport: NetworkTransport {
3757
/// - files: An array of `GraphQLFile` objects to send.
3858
/// - context: [optional] A context that is being passed through the request chain.
3959
/// - Returns: A stream of `GraphQLResult`s for each response.
60+
#warning("TODO: should support query and mutation as seperate functions")
4061
func upload<Operation: GraphQLOperation>(
4162
operation: Operation,
4263
files: [GraphQLFile],
4364
context: (any RequestContext)?
44-
) async throws -> AsyncThrowingStream<GraphQLResult<Operation.Data>, any Error>
65+
) throws -> AsyncThrowingStream<GraphQLResult<Operation.Data>, any Error>
4566
}

apollo-ios/Sources/Apollo/RequestChain.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ struct RequestChain<Request: GraphQLRequest>: Sendable {
7474
}
7575
}
7676
}
77-
77+
7878
func kickoff(
7979
request: Request
8080
) -> ResultStream {

apollo-ios/Sources/Apollo/RequestChainNetworkTransport.swift

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import Foundation
2+
23
#if !COCOAPODS
3-
import ApolloAPI
4+
import ApolloAPI
45
#endif
56

67
/// An implementation of `NetworkTransport` which creates a `RequestChain` object
@@ -9,7 +10,7 @@ public final class RequestChainNetworkTransport: NetworkTransport, Sendable {
910

1011
/// The interceptor provider to use when constructing a request chain
1112
let interceptorProvider: any InterceptorProvider
12-
13+
1314
/// The GraphQL endpoint URL to use.
1415
public let endpointURL: URL
1516

@@ -74,7 +75,7 @@ public final class RequestChainNetworkTransport: NetworkTransport, Sendable {
7475
self.useGETForQueries = useGETForQueries
7576
self.sendEnhancedClientAwareness = sendEnhancedClientAwareness
7677
}
77-
78+
7879
/// Constructs a GraphQL request for the given operation.
7980
///
8081
/// Override this method if you need to use a custom subclass of `HTTPRequest`.
@@ -107,20 +108,39 @@ public final class RequestChainNetworkTransport: NetworkTransport, Sendable {
107108
request.addHeaders(self.additionalHeaders)
108109
return request
109110
}
110-
111+
111112
// MARK: - NetworkTransport Conformance
112113

113-
public func send<Operation: GraphQLOperation>(
114-
operation: Operation,
114+
public func send<Query: GraphQLQuery>(
115+
query: Query,
115116
cachePolicy: CachePolicy,
116117
contextIdentifier: UUID? = nil,
117118
context: (any RequestContext)? = nil
118-
) throws -> AsyncThrowingStream<GraphQLResult<Operation.Data>, any Error> {
119+
) throws -> AsyncThrowingStream<GraphQLResult<Query.Data>, any Error> {
120+
let request = self.constructRequest(
121+
for: query,
122+
cachePolicy: cachePolicy,
123+
contextIdentifier: contextIdentifier,
124+
context: context
125+
)
126+
127+
let chain = makeChain(for: request)
128+
129+
return chain.kickoff(request: request)
130+
}
131+
132+
public func send<Mutation: GraphQLMutation>(
133+
mutation: Mutation,
134+
cachePolicy: CachePolicy,
135+
contextIdentifier: UUID? = nil,
136+
context: (any RequestContext)? = nil
137+
) throws -> AsyncThrowingStream<GraphQLResult<Mutation.Data>, any Error> {
119138
let request = self.constructRequest(
120-
for: operation,
139+
for: mutation,
121140
cachePolicy: cachePolicy,
122141
contextIdentifier: contextIdentifier,
123-
context: context)
142+
context: context
143+
)
124144

125145
let chain = makeChain(for: request)
126146

@@ -139,11 +159,11 @@ public final class RequestChainNetworkTransport: NetworkTransport, Sendable {
139159
)
140160
return chain
141161
}
142-
162+
143163
}
144164

145165
extension RequestChainNetworkTransport: UploadingNetworkTransport {
146-
166+
147167
/// Constructs an uploading (ie, multipart) GraphQL request
148168
///
149169
/// Override this method if you need to use a custom subclass of `HTTPRequest`.
@@ -152,7 +172,7 @@ extension RequestChainNetworkTransport: UploadingNetworkTransport {
152172
/// - operation: The operation to create a request for
153173
/// - files: The files you wish to upload
154174
/// - context: [optional] A context that is being passed through the request chain. Should default to `nil`.
155-
/// - manualBoundary: [optional] A manually set boundary for your upload request. Defaults to nil.
175+
/// - manualBoundary: [optional] A manually set boundary for your upload request. Defaults to nil.
156176
/// - Returns: The created request.
157177
public func constructUploadRequest<Operation: GraphQLOperation>(
158178
for operation: Operation,
@@ -174,12 +194,12 @@ extension RequestChainNetworkTransport: UploadingNetworkTransport {
174194
request.additionalHeaders = self.additionalHeaders
175195
return request
176196
}
177-
197+
178198
public func upload<Operation: GraphQLOperation>(
179199
operation: Operation,
180200
files: [GraphQLFile],
181201
context: (any RequestContext)?
182-
) async throws -> AsyncThrowingStream<GraphQLResult<Operation.Data>, any Error> {
202+
) throws -> AsyncThrowingStream<GraphQLResult<Operation.Data>, any Error> {
183203
let request = self.constructUploadRequest(for: operation, with: files, context: context)
184204
let chain = makeChain(for: request)
185205
return chain.kickoff(request: request)

apollo-ios/Sources/ApolloWebSocket/SplitNetworkTransport.swift

Lines changed: 60 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
import Foundation
2+
23
#if !COCOAPODS
3-
import Apollo
4-
import ApolloAPI
4+
import Apollo
5+
import ApolloAPI
56
#endif
67

7-
#warning("""
8-
TODO: This is messy. Why is http network transport called "uploadingNetworkTransport"?
9-
Websocket transport should be typesafe to a protocol that guaruntees it supports web sockets/ subscriptions
10-
""")
8+
#warning(
9+
"""
10+
TODO: This is messy. Why is http network transport called "uploadingNetworkTransport"?
11+
Websocket transport should be typesafe to a protocol that guaruntees it supports web sockets/ subscriptions
12+
"""
13+
)
1114
/// A network transport that sends subscriptions using one `NetworkTransport` and other requests using another `NetworkTransport`. Ideal for sending subscriptions via a web socket but everything else via HTTP.
1215
public final class SplitNetworkTransport: Sendable {
1316
private let uploadingNetworkTransport: any UploadingNetworkTransport
14-
private let webSocketNetworkTransport: any NetworkTransport
17+
private let webSocketNetworkTransport: any SubscriptionNetworkTransport
1518

1619
public var clientName: String {
1720
let httpName = self.uploadingNetworkTransport.clientName
@@ -32,13 +35,16 @@ public final class SplitNetworkTransport: Sendable {
3235
return "SPLIT_HTTPVERSION_\(httpVersion)_WEBSOCKETVERSION_\(websocketVersion)"
3336
}
3437
}
35-
38+
3639
/// Designated initializer
3740
///
3841
/// - Parameters:
3942
/// - uploadingNetworkTransport: An `UploadingNetworkTransport` to use for non-subscription requests. Should generally be a `RequestChainNetworkTransport` or something similar.
4043
/// - webSocketNetworkTransport: A `NetworkTransport` to use for subscription requests. Should generally be a `WebSocketTransport` or something similar.
41-
public init(uploadingNetworkTransport: any UploadingNetworkTransport, webSocketNetworkTransport: any NetworkTransport) {
44+
public init(
45+
uploadingNetworkTransport: any UploadingNetworkTransport,
46+
webSocketNetworkTransport: any SubscriptionNetworkTransport
47+
) {
4248
self.uploadingNetworkTransport = uploadingNetworkTransport
4349
self.webSocketNetworkTransport = webSocketNetworkTransport
4450
}
@@ -48,25 +54,50 @@ public final class SplitNetworkTransport: Sendable {
4854

4955
extension SplitNetworkTransport: NetworkTransport {
5056

51-
public func send<Operation: GraphQLOperation>(
52-
operation: Operation,
57+
public func send<Query>(
58+
query: Query,
5359
cachePolicy: CachePolicy,
54-
contextIdentifier: UUID? = nil,
55-
context: (any RequestContext)? = nil
56-
) throws -> AsyncThrowingStream<GraphQLResult<Operation.Data>, any Error> {
57-
if Operation.operationType == .subscription {
58-
return try webSocketNetworkTransport.send(
59-
operation: operation,
60-
cachePolicy: cachePolicy,
61-
contextIdentifier: contextIdentifier,
62-
context: context)
63-
} else {
64-
return try uploadingNetworkTransport.send(
65-
operation: operation,
66-
cachePolicy: cachePolicy,
67-
contextIdentifier: contextIdentifier,
68-
context: context)
69-
}
60+
contextIdentifier: UUID?,
61+
context: (any RequestContext)?
62+
) throws -> AsyncThrowingStream<GraphQLResult<Query.Data>, any Error> where Query: GraphQLQuery {
63+
return try uploadingNetworkTransport.send(
64+
query: query,
65+
cachePolicy: cachePolicy,
66+
contextIdentifier: contextIdentifier,
67+
context: context
68+
)
69+
}
70+
71+
public func send<Mutation>(
72+
mutation: Mutation,
73+
cachePolicy: CachePolicy,
74+
contextIdentifier: UUID?,
75+
context: (any RequestContext)?
76+
) throws -> AsyncThrowingStream<GraphQLResult<Mutation.Data>, any Error> where Mutation: GraphQLMutation {
77+
return try uploadingNetworkTransport.send(
78+
mutation: mutation,
79+
cachePolicy: cachePolicy,
80+
contextIdentifier: contextIdentifier,
81+
context: context
82+
)
83+
}
84+
}
85+
86+
// MARK: - SubscriptionNetworkTransport conformance
87+
88+
extension SplitNetworkTransport: SubscriptionNetworkTransport {
89+
public func send<Subscription>(
90+
subscription: Subscription,
91+
cachePolicy: CachePolicy,
92+
contextIdentifier: UUID?,
93+
context: (any RequestContext)?
94+
) throws -> AsyncThrowingStream<GraphQLResult<Subscription.Data>, any Error> where Subscription: GraphQLSubscription {
95+
return try webSocketNetworkTransport.send(
96+
subscription: subscription,
97+
cachePolicy: cachePolicy,
98+
contextIdentifier: contextIdentifier,
99+
context: context
100+
)
70101
}
71102
}
72103

@@ -78,8 +109,8 @@ extension SplitNetworkTransport: UploadingNetworkTransport {
78109
operation: Operation,
79110
files: [GraphQLFile],
80111
context: (any RequestContext)?
81-
) async throws -> AsyncThrowingStream<GraphQLResult<Operation.Data>, any Error> {
82-
return try await uploadingNetworkTransport.upload(
112+
) throws -> AsyncThrowingStream<GraphQLResult<Operation.Data>, any Error> {
113+
return try uploadingNetworkTransport.upload(
83114
operation: operation,
84115
files: files,
85116
context: context

0 commit comments

Comments
 (0)