Skip to content

Commit 96305f8

Browse files
Merge pull request #644 from apollographql/change/result-type-returns
`Result` ALL THE THINGS!!!
2 parents cb05c7f + c8b94d8 commit 96305f8

19 files changed

Lines changed: 880 additions & 532 deletions

Sources/Apollo/ApolloClient.swift

Lines changed: 48 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,8 @@ public enum CachePolicy {
1818
/// A handler for operation results.
1919
///
2020
/// - Parameters:
21-
/// - result: The result of the performed operation, or `nil` if an error occurred.
22-
/// - error: An error that indicates why the mutation failed, or `nil` if the mutation was succesful.
23-
public typealias GraphQLResultHandler<Data> = (_ result: GraphQLResult<Data>?, _ error: Error?) -> Void
24-
25-
@available(*, deprecated, renamed: "GraphQLResultHandler")
26-
public typealias OperationResultHandler<Operation: GraphQLOperation> = GraphQLResultHandler<Operation.Data>
21+
/// - result: The result of a performed operation. Will have a `GraphQLResult` with any parsed data and any GraphQL errors on `success`, and an `Error` on `failure`.
22+
public typealias GraphQLResultHandler<Data> = (Result<GraphQLResult<Data>, Error>) -> Void
2723

2824
/// The `ApolloClient` class provides the core API for Apollo. This API provides methods to fetch and watch queries, and to perform mutations.
2925
public class ApolloClient {
@@ -103,7 +99,6 @@ public class ApolloClient {
10399
/// - queue: A dispatch queue on which the result handler will be called. Defaults to the main queue.
104100
/// - resultHandler: An optional closure that is called when query results are available or when an error occurs.
105101
/// - Returns: A query watcher object that can be used to control the watching behavior.
106-
107102
public func watch<Query: GraphQLQuery>(query: Query, cachePolicy: CachePolicy = .returnCacheDataElseFetch, queue: DispatchQueue = DispatchQueue.main, resultHandler: @escaping GraphQLResultHandler<Query.Data>) -> GraphQLQueryWatcher<Query> {
108103
let watcher = GraphQLQueryWatcher(client: self, query: query, resultHandler: wrapResultHandler(resultHandler, queue: queue))
109104
watcher.fetch(cachePolicy: cachePolicy)
@@ -135,47 +130,47 @@ public class ApolloClient {
135130
}
136131

137132
fileprivate func send<Operation: GraphQLOperation>(operation: Operation, shouldPublishResultToStore: Bool, context: UnsafeMutableRawPointer?, resultHandler: @escaping GraphQLResultHandler<Operation.Data>) -> Cancellable {
138-
return networkTransport.send(operation: operation) { (response, error) in
139-
guard let response = response else {
140-
resultHandler(nil, error)
141-
return
142-
}
143-
144-
// If there is no need to publish the result to the store, we can use a fast path.
145-
if !shouldPublishResultToStore {
146-
do {
147-
let result = try response.parseResultFast()
148-
resultHandler(result, nil)
149-
} catch {
150-
resultHandler(nil, error)
151-
}
152-
return
153-
}
154-
155-
firstly {
156-
try response.parseResult(cacheKeyForObject: self.cacheKeyForObject)
157-
}.andThen { (result, records) in
158-
if let records = records {
159-
self.store.publish(records: records, context: context).catch { error in
160-
preconditionFailure(String(describing: error))
133+
return networkTransport.send(operation: operation) { result in
134+
switch result {
135+
case .failure(let error):
136+
resultHandler(.failure(error))
137+
case .success(let response):
138+
// If there is no need to publish the result to the store, we can use a fast path.
139+
if !shouldPublishResultToStore {
140+
do {
141+
let result = try response.parseResultFast()
142+
resultHandler(.success(result))
143+
} catch {
144+
resultHandler(.failure(error))
161145
}
146+
return
147+
}
148+
149+
firstly {
150+
try response.parseResult(cacheKeyForObject: self.cacheKeyForObject)
151+
}.andThen { (result, records) in
152+
if let records = records {
153+
self.store.publish(records: records, context: context).catch { error in
154+
preconditionFailure(String(describing: error))
155+
}
156+
}
157+
resultHandler(.success(result))
158+
}.catch { error in
159+
resultHandler(.failure(error))
162160
}
163-
resultHandler(result, nil)
164-
}.catch { error in
165-
resultHandler(nil, error)
166161
}
167162
}
168163
}
169164
}
170165

171166
private func wrapResultHandler<Data>(_ resultHandler: GraphQLResultHandler<Data>?, queue handlerQueue: DispatchQueue) -> GraphQLResultHandler<Data> {
172167
guard let resultHandler = resultHandler else {
173-
return { _, _ in }
168+
return { _ in }
174169
}
175170

176-
return { (result, error) in
171+
return { result in
177172
handlerQueue.async {
178-
resultHandler(result, error)
173+
resultHandler(result)
179174
}
180175
}
181176
}
@@ -210,34 +205,35 @@ private final class FetchQueryOperation<Query: GraphQLQuery>: AsynchronousOperat
210205
return
211206
}
212207

213-
client.store.load(query: query) { (result, error) in
214-
if error == nil {
215-
self.resultHandler(result, nil)
216-
217-
if self.cachePolicy != .returnCacheDataAndFetch {
218-
self.state = .finished
219-
return
220-
}
221-
}
222-
208+
client.store.load(query: query) { result in
223209
if self.isCancelled {
224210
self.state = .finished
225211
return
226212
}
227213

228-
if self.cachePolicy == .returnCacheDataDontFetch {
229-
self.resultHandler(nil, nil)
230-
self.state = .finished
231-
return
214+
switch result {
215+
case .success:
216+
self.resultHandler(result)
217+
218+
if self.cachePolicy != .returnCacheDataAndFetch {
219+
self.state = .finished
220+
return
221+
}
222+
case .failure:
223+
if self.cachePolicy == .returnCacheDataDontFetch {
224+
self.resultHandler(result)
225+
self.state = .finished
226+
return
227+
}
232228
}
233229

234230
self.fetchFromNetwork()
235231
}
236232
}
237233

238234
func fetchFromNetwork() {
239-
networkTask = client.send(operation: query, shouldPublishResultToStore: true, context: context) { (result, error) in
240-
self.resultHandler(result, error)
235+
networkTask = client.send(operation: query, shouldPublishResultToStore: true, context: context) { result in
236+
self.resultHandler(result)
241237
self.state = .finished
242238
return
243239
}

Sources/Apollo/ApolloStore.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,9 @@ public final class ApolloStore {
131131

132132
public func load<Query: GraphQLQuery>(query: Query, resultHandler: @escaping GraphQLResultHandler<Query.Data>) {
133133
load(query: query).andThen { result in
134-
resultHandler(result, nil)
134+
resultHandler(.success(result))
135135
}.catch { error in
136-
resultHandler(nil, error)
136+
resultHandler(.failure(error))
137137
}
138138
}
139139

Sources/Apollo/GraphQLQueryWatcher.swift

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,17 @@ public final class GraphQLQueryWatcher<Query: GraphQLQuery>: Cancellable, Apollo
2626
}
2727

2828
func fetch(cachePolicy: CachePolicy) {
29-
fetching = client?.fetch(query: query, cachePolicy: cachePolicy, context: &context) { [weak self] (result, error) in
29+
fetching = client?.fetch(query: query, cachePolicy: cachePolicy, context: &context) { [weak self] result in
3030
guard let `self` = self else { return }
31-
32-
self.dependentKeys = result?.dependentKeys
33-
self.resultHandler(result, error)
31+
32+
switch result {
33+
case .success(let graphQLResult):
34+
self.dependentKeys = graphQLResult.dependentKeys
35+
case .failure:
36+
break
37+
}
38+
39+
self.resultHandler(result)
3440
}
3541
}
3642

Sources/Apollo/HTTPNetworkTransport.swift

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public protocol HTTPNetworkTransportRetryDelegate: HTTPNetworkTransportDelegate
6868
// MARK: -
6969

7070
/// A network transport that uses HTTP POST requests to send GraphQL operations to a server, and that uses `URLSession` as the networking implementation.
71-
public class HTTPNetworkTransport: NetworkTransport {
71+
public class HTTPNetworkTransport {
7272
let url: URL
7373
let session: URLSession
7474
let serializationFormat = JSONSerializationFormat.self
@@ -96,35 +96,23 @@ public class HTTPNetworkTransport: NetworkTransport {
9696
self.delegate = delegate
9797
}
9898

99-
/// Send a GraphQL operation to a server and return a response.
100-
///
101-
/// - Parameters:
102-
/// - operation: The operation to send.
103-
/// - completionHandler: A closure to call when a request completes.
104-
/// - response: The response received from the server, or `nil` if an error occurred.
105-
/// - error: An error that indicates why a request failed, or `nil` if the request was succesful.
106-
/// - Returns: An object that can be used to cancel an in progress request.
107-
public func send<Operation>(operation: Operation, completionHandler: @escaping (_ response: GraphQLResponse<Operation>?, _ error: Error?) -> Void) -> Cancellable {
108-
return send(operation: operation, files: nil, completionHandler: completionHandler)
109-
}
110-
11199
/// Uploads the given files with the given operation.
112100
///
113101
/// - Parameters:
114102
/// - operation: The operation to send
115103
/// - files: An array of `GraphQLFile` objects to send.
116104
/// - completionHandler: The completion handler to execute when the request completes or errors
117105
/// - Returns: An object that can be used to cancel an in progress request.
118-
public func upload<Operation>(operation: Operation, files: [GraphQLFile], completionHandler: @escaping (_ response: GraphQLResponse<Operation>?, _ error: Error?) -> Void) -> Cancellable {
106+
public func upload<Operation>(operation: Operation, files: [GraphQLFile], completionHandler: @escaping (_ result: Result<GraphQLResponse<Operation>, Error>) -> Void) -> Cancellable {
119107
return send(operation: operation, files: files, completionHandler: completionHandler)
120108
}
121109

122-
private func send<Operation>(operation: Operation, files: [GraphQLFile]?, completionHandler: @escaping (_ response: GraphQLResponse<Operation>?, _ error: Error?) -> Void) -> Cancellable {
110+
private func send<Operation>(operation: Operation, files: [GraphQLFile]?, completionHandler: @escaping (_ results: Result<GraphQLResponse<Operation>, Error>) -> Void) -> Cancellable {
123111
let request: URLRequest
124112
do {
125113
request = try self.createRequest(for: operation, files: files)
126114
} catch {
127-
completionHandler(nil, error)
115+
completionHandler(.failure(error))
128116
return EmptyCancellable()
129117
}
130118

@@ -176,7 +164,7 @@ public class HTTPNetworkTransport: NetworkTransport {
176164
throw GraphQLHTTPResponseError(body: data, response: httpResponse, kind: .invalidResponse)
177165
}
178166
let response = GraphQLResponse(operation: operation, body: body)
179-
completionHandler(response, nil)
167+
completionHandler(.success(response))
180168
} catch let parsingError {
181169
self?.handleErrorOrRetry(operation: operation,
182170
error: parsingError,
@@ -195,11 +183,11 @@ public class HTTPNetworkTransport: NetworkTransport {
195183
error: Error,
196184
for request: URLRequest,
197185
response: URLResponse?,
198-
completionHandler: @escaping (_ response: GraphQLResponse<Operation>?, _ error: Error?) -> Void) {
186+
completionHandler: @escaping (_ result: Result<GraphQLResponse<Operation>, Error>) -> Void) {
199187
guard
200188
let delegate = self.delegate,
201189
let retrier = delegate as? HTTPNetworkTransportRetryDelegate else {
202-
completionHandler(nil, error)
190+
completionHandler(.failure(error))
203191
return
204192
}
205193

@@ -210,7 +198,7 @@ public class HTTPNetworkTransport: NetworkTransport {
210198
response: response,
211199
retryHandler: { [weak self] shouldRetry in
212200
guard shouldRetry else {
213-
completionHandler(nil, error)
201+
completionHandler(.failure(error))
214202
return
215203
}
216204

@@ -290,3 +278,12 @@ public class HTTPNetworkTransport: NetworkTransport {
290278
return request
291279
}
292280
}
281+
282+
// MARK: - NetworkTransport conformance
283+
284+
extension HTTPNetworkTransport: NetworkTransport {
285+
286+
public func send<Operation>(operation: Operation, completionHandler: @escaping (_ result: Result<GraphQLResponse<Operation>, Error>) -> Void) -> Cancellable {
287+
return send(operation: operation, files: nil, completionHandler: completionHandler)
288+
}
289+
}
Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
/// A network transport is responsible for sending GraphQL operations to a server.
22
public protocol NetworkTransport {
3+
34
/// Send a GraphQL operation to a server and return a response.
45
///
56
/// - Parameters:
67
/// - operation: The operation to send.
7-
/// - completionHandler: A closure to call when a request completes.
8-
/// - response: The response received from the server, or `nil` if an error occurred.
9-
/// - error: An error that indicates why a request failed, or `nil` if the request was succesful.
8+
/// - completionHandler: A closure to call when a request completes. On `success` will contain the response received from the server. On `failure` will contain the error which occurred.
109
/// - Returns: An object that can be used to cancel an in progress request.
11-
func send<Operation>(operation: Operation, completionHandler: @escaping (_ response: GraphQLResponse<Operation>?, _ error: Error?) -> Void) -> Cancellable
10+
func send<Operation>(operation: Operation, completionHandler: @escaping (_ result: Result<GraphQLResponse<Operation>, Error>) -> Void) -> Cancellable
1211
}

Sources/ApolloWebSocket/SplitNetworkTransport.swift

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,31 @@
22
import Apollo
33
#endif
44

5-
6-
public class SplitNetworkTransport: NetworkTransport {
5+
/// 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.
6+
public class SplitNetworkTransport {
77
private let httpNetworkTransport: NetworkTransport
88
private let webSocketNetworkTransport: NetworkTransport
99

10+
/// Designated initializer
11+
///
12+
/// - Parameters:
13+
/// - httpNetworkTransport: A `NetworkTransport` to use for non-subscription requests. Should generally be a `HTTPNetworkTransport` or something similar.
14+
/// - webSocketNetworkTransport: A `NetworkTransport` to use for subscription requests. Should generally be a `WebSocketTransport` or something similar.
1015
public init(httpNetworkTransport: NetworkTransport, webSocketNetworkTransport: NetworkTransport) {
1116
self.httpNetworkTransport = httpNetworkTransport
1217
self.webSocketNetworkTransport = webSocketNetworkTransport
1318
}
19+
}
20+
21+
// MARK: - NetworkTransport conformance
22+
23+
extension SplitNetworkTransport: NetworkTransport {
1424

15-
public func send<Operation>(operation: Operation, completionHandler: @escaping (GraphQLResponse<Operation>?, Error?) -> Void) -> Cancellable {
25+
public func send<Operation>(operation: Operation, completionHandler: @escaping (Result<GraphQLResponse<Operation>, Error>) -> Void) -> Cancellable {
1626
if operation.operationType == .subscription {
17-
return webSocketNetworkTransport.send(operation: operation, completionHandler: completionHandler)
27+
return webSocketNetworkTransport.send(operation: operation, completionHandler: completionHandler)
1828
} else {
19-
return httpNetworkTransport.send(operation: operation, completionHandler: completionHandler)
29+
return httpNetworkTransport.send(operation: operation, completionHandler: completionHandler)
2030
}
2131
}
2232
}

0 commit comments

Comments
 (0)