Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add background-compatible URLSessionClient class #1163

Merged
merged 12 commits into from
Apr 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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