Skip to content

Commit

Permalink
Merge pull request #1163 from apollographql/add/urlsessionclient
Browse files Browse the repository at this point in the history
Add background-compatible `URLSessionClient` class
  • Loading branch information
designatednerd authored Apr 27, 2020
2 parents b91ccf6 + d68300e commit 95eaacb
Show file tree
Hide file tree
Showing 9 changed files with 612 additions and 131 deletions.
12 changes: 12 additions & 0 deletions Apollo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
9B21FD772422C8CC00998B5C /* TestFileHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B21FD762422C8CC00998B5C /* TestFileHelper.swift */; };
9B21FD782424305700998B5C /* ExpectedEnumWithDifferentCases.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B68F05F2416F80C00E97318 /* ExpectedEnumWithDifferentCases.swift */; };
9B21FD792424305E00998B5C /* ExpectedEnumWithSanitizedCases.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B68F063241703B200E97318 /* ExpectedEnumWithSanitizedCases.swift */; };
9B4F453F244A27B900C2CF7D /* URLSessionClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B4F453E244A27B900C2CF7D /* URLSessionClient.swift */; };
9B4F4541244A2A9200C2CF7D /* HTTPBinAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B4F4540244A2A9200C2CF7D /* HTTPBinAPI.swift */; };
9B4F4543244A2AD300C2CF7D /* URLSessionClientTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B4F4542244A2AD300C2CF7D /* URLSessionClientTests.swift */; };
9B518C87235F819E004C426D /* CLIDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B518C85235F8125004C426D /* CLIDownloader.swift */; };
9B518C8C235F8B5F004C426D /* ApolloFilePathHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B518C8A235F8B05004C426D /* ApolloFilePathHelper.swift */; };
9B518C8D235F8B9E004C426D /* CLIDownloaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B518C88235F8AD4004C426D /* CLIDownloaderTests.swift */; };
Expand Down Expand Up @@ -365,6 +368,9 @@
9B21FD742422C29D00998B5C /* GraphQLFileTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphQLFileTests.swift; sourceTree = "<group>"; };
9B21FD762422C8CC00998B5C /* TestFileHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestFileHelper.swift; sourceTree = "<group>"; };
9B4AA8AD239EFDC9003E1300 /* Apollo-Target-CodegenTests.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Apollo-Target-CodegenTests.xcconfig"; sourceTree = "<group>"; };
9B4F453E244A27B900C2CF7D /* URLSessionClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLSessionClient.swift; sourceTree = "<group>"; };
9B4F4540244A2A9200C2CF7D /* HTTPBinAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPBinAPI.swift; sourceTree = "<group>"; };
9B4F4542244A2AD300C2CF7D /* URLSessionClientTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLSessionClientTests.swift; sourceTree = "<group>"; };
9B518C85235F8125004C426D /* CLIDownloader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CLIDownloader.swift; sourceTree = "<group>"; };
9B518C88235F8AD4004C426D /* CLIDownloaderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CLIDownloaderTests.swift; sourceTree = "<group>"; };
9B518C8A235F8B05004C426D /* ApolloFilePathHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApolloFilePathHelper.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -727,6 +733,7 @@
C3279FC52345233000224790 /* TestCustomRequestCreator.swift */,
9B64F6752354D219002D1BB5 /* URL+QueryDict.swift */,
9B21FD762422C8CC00998B5C /* TestFileHelper.swift */,
9B4F4540244A2A9200C2CF7D /* HTTPBinAPI.swift */,
);
name = TestHelpers;
sourceTree = "<group>";
Expand Down Expand Up @@ -1169,6 +1176,7 @@
9FF90A6B1DDDEB420034C3B6 /* ReadFieldValueTests.swift */,
C338DF1622DD9DE9006AF33E /* RequestCreatorTests.swift */,
9F19D8451EED8D3B00C57247 /* ResultOrPromiseTests.swift */,
9B4F4542244A2AD300C2CF7D /* URLSessionClientTests.swift */,
5BB2C0222380836100774170 /* VersionNumberTests.swift */,
C304EBD322DDC7B200748F72 /* a.txt */,
C35D43BE22DDD3C100BCBABE /* b.txt */,
Expand Down Expand Up @@ -1203,6 +1211,7 @@
C377CCAA22D7992E00572E03 /* MultipartFormData.swift */,
9F69FFA81D42855900E000B1 /* NetworkTransport.swift */,
9BEDC79D22E5D2CF00549BF6 /* RequestCreator.swift */,
9B4F453E244A27B900C2CF7D /* URLSessionClient.swift */,
);
name = Network;
sourceTree = "<group>";
Expand Down Expand Up @@ -1997,6 +2006,7 @@
5AC6CA4322AAF7B200B7C94D /* GraphQLHTTPMethod.swift in Sources */,
9FE941D01E62C771007CDD89 /* Promise.swift in Sources */,
9BA1245E22DE116B00BF1D24 /* Result+Helpers.swift in Sources */,
9B4F453F244A27B900C2CF7D /* URLSessionClient.swift in Sources */,
9FC750631D2A59F600458D91 /* ApolloClient.swift in Sources */,
9BA3130E2302BEA5007B7FC5 /* DispatchQueue+Optional.swift in Sources */,
9F86B6901E65533D00B885FF /* GraphQLResponseGenerator.swift in Sources */,
Expand All @@ -2013,6 +2023,7 @@
9FC9A9C81E2EFE6E0023C4D5 /* CacheKeyForFieldTests.swift in Sources */,
9F91CF8F1F6C0DB2008DD0BE /* MutatingResultsTests.swift in Sources */,
F82E62E122BCD223000C311B /* AutomaticPersistedQueriesTests.swift in Sources */,
9B4F4543244A2AD300C2CF7D /* URLSessionClientTests.swift in Sources */,
9F19D8461EED8D3B00C57247 /* ResultOrPromiseTests.swift in Sources */,
9F533AB31E6C4A4200CBE097 /* BatchedLoadTests.swift in Sources */,
C3279FC72345234D00224790 /* TestCustomRequestCreator.swift in Sources */,
Expand All @@ -2030,6 +2041,7 @@
9FF90A711DDDEB420034C3B6 /* ReadFieldValueTests.swift in Sources */,
9F295E311E27534800A24949 /* NormalizeQueryResults.swift in Sources */,
9FF90A731DDDEB420034C3B6 /* ParseQueryResponseTests.swift in Sources */,
9B4F4541244A2A9200C2CF7D /* HTTPBinAPI.swift in Sources */,
9BF1A94F22CA5784005292C2 /* HTTPTransportTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
125 changes: 52 additions & 73 deletions Sources/Apollo/HTTPNetworkTransport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ public class HTTPNetworkTransport {
}

let url: URL
let session: URLSession
let client: URLSessionClient
let serializationFormat = JSONSerializationFormat.self
let useGETForQueries: Bool
let enableAutoPersistedQueries: Bool
Expand All @@ -127,14 +127,14 @@ public class HTTPNetworkTransport {
/// - enableAutoPersistedQueries: Whether to send persistedQuery extension. QueryDocument will be absent at 1st request, retry with QueryDocument if server respond PersistedQueryNotFound or PersistedQueryNotSupport. Defaults to false.
/// - useGETForPersistedQueryRetry: Whether to retry persistedQuery request with HttpGetMethod. Defaults to false.
public init(url: URL,
session: URLSession = .shared,
client: URLSessionClient = URLSessionClient(),
sendOperationIdentifiers: Bool = false,
useGETForQueries: Bool = false,
enableAutoPersistedQueries: Bool = false,
useGETForPersistedQueryRetry: Bool = false,
requestCreator: RequestCreator = ApolloRequestCreator()) {
self.url = url
self.session = session
self.client = client
self.sendOperationIdentifiers = sendOperationIdentifiers
self.useGETForQueries = useGETForQueries
self.enableAutoPersistedQueries = enableAutoPersistedQueries
Expand All @@ -155,88 +155,67 @@ public class HTTPNetworkTransport {
completionHandler(.failure(error))
return EmptyCancellable()
}

let task = session.dataTask(with: request) { [weak self] data, response, error in

let task = self.client.sendRequest(request, rawTaskCompletionHandler: { [weak self] data, response, error in
self?.rawTaskCompleted(request: request, data: data, response: response, error: error)
}, completion: { [weak self] result in
guard let self = self else {
// None of the rest of this really matters
return
}

self.rawTaskCompleted(request: request,
data: data,
response: response,
error: error)

if let receivedError = error {
self.handleErrorOrRetry(operation: operation,
files: files,
error: receivedError,
for: request,
response: response,
completionHandler: completionHandler)
return
}

guard let httpResponse = response as? HTTPURLResponse else {
fatalError("Response should be an HTTPURLResponse")
}

guard httpResponse.isSuccessful else {
let unsuccessfulError = GraphQLHTTPResponseError(body: data,
response: httpResponse,
kind: .errorResponse)
self.handleErrorOrRetry(operation: operation,
files: files,
error: unsuccessfulError,
for: request,
response: response,
completionHandler: completionHandler)
return
}

guard let data = data else {
let error = GraphQLHTTPResponseError(body: nil,
response: httpResponse,
kind: .invalidResponse)

switch result {
case .failure(let error):
self.handleErrorOrRetry(operation: operation,
files: files,
error: error,
for: request,
response: response,
response: nil,
completionHandler: completionHandler)
return
}

do {
guard let body = try self.serializationFormat.deserialize(data: data) as? JSONObject else {
throw GraphQLHTTPResponseError(body: data, response: httpResponse, kind: .invalidResponse)
case .success(let (data, httpResponse)):
guard httpResponse.isSuccessful == true else {
let unsuccessfulError = GraphQLHTTPResponseError(body: data,
response: httpResponse,
kind: .errorResponse)
self.handleErrorOrRetry(operation: operation,
files: files,
error: unsuccessfulError,
for: request,
response: httpResponse,
completionHandler: completionHandler)
return
}

let graphQLResponse = GraphQLResponse(operation: operation, body: body)

if let errors = graphQLResponse.parseErrorsOnlyFast() {
// Handle specific errors from response
self.handleGraphQLErrorsIfNeeded(operation: operation,
files: files,
for: request,
body: body,
errors: errors,
completionHandler: completionHandler)
} else {
completionHandler(.success(graphQLResponse))

do {
guard let body = try self.serializationFormat.deserialize(data: data) as? JSONObject else {
throw GraphQLHTTPResponseError(body: data, response: httpResponse, kind: .invalidResponse)
}

let graphQLResponse = GraphQLResponse(operation: operation, body: body)

if let errors = graphQLResponse.parseErrorsOnlyFast() {
// Handle specific errors from response
self.handleGraphQLErrorsIfNeeded(operation: operation,
files: files,
for: request,
body: body,
errors: errors,
completionHandler: completionHandler)
} else {
completionHandler(.success(graphQLResponse))
}
} catch let parsingError {
self.handleErrorOrRetry(operation: operation,
files: files,
error: parsingError,
for: request,
response: httpResponse,
completionHandler: completionHandler)
}
} catch let parsingError {
self.handleErrorOrRetry(operation: operation,
files: files,
error: parsingError,
for: request,
response: response,
completionHandler: completionHandler)
}
}

task.resume()
})

// Task is resumed by underlying framework
return task
}

Expand Down Expand Up @@ -485,7 +464,7 @@ extension HTTPNetworkTransport: Equatable {

public static func ==(lhs: HTTPNetworkTransport, rhs: HTTPNetworkTransport) -> Bool {
return lhs.url == rhs.url
&& lhs.session == rhs.session
&& lhs.client == rhs.client
&& lhs.sendOperationIdentifiers == rhs.sendOperationIdentifiers
&& lhs.useGETForQueries == rhs.useGETForQueries
}
Expand Down
Loading

0 comments on commit 95eaacb

Please sign in to comment.