Skip to content

Commit a91fccd

Browse files
Merge pull request #1489 from apollographql/add/invalidation-error
Check that `URLSessionClient` hasn't been invalidated before sending
2 parents a0e0e27 + 9df9f04 commit a91fccd

3 files changed

Lines changed: 47 additions & 1 deletion

File tree

Sources/Apollo/NetworkFetchInterceptor.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,11 @@ public class NetworkFetchInterceptor: ApolloInterceptor, Cancellable {
2929
return
3030
}
3131

32-
self.currentTask = self.client.sendRequest(urlRequest) { result in
32+
self.currentTask = self.client.sendRequest(urlRequest) { [weak self] result in
33+
guard let self = self else {
34+
return
35+
}
36+
3337
defer {
3438
self.currentTask = nil
3539
}

Sources/Apollo/URLSessionClient.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ open class URLSessionClient: NSObject, URLSessionDelegate, URLSessionTaskDelegat
1818
case sessionBecameInvalidWithoutUnderlyingError
1919
case dataForRequestNotFound(request: URLRequest?)
2020
case networkError(data: Data, response: HTTPURLResponse?, underlying: Error)
21+
case sessionInvalidated
2122

2223
public var errorDescription: String? {
2324
switch self {
@@ -29,6 +30,8 @@ open class URLSessionClient: NSObject, URLSessionDelegate, URLSessionTaskDelegat
2930
return "URLSessionClient was not able to locate the stored data for request \(String(describing: request))"
3031
case .networkError(_, _, let underlyingError):
3132
return "A network error occurred: \(underlyingError.localizedDescription)"
33+
case .sessionInvalidated:
34+
return "Attempting to create a new request after the session has been invalidated!"
3235
}
3336
}
3437
}
@@ -44,6 +47,12 @@ open class URLSessionClient: NSObject, URLSessionDelegate, URLSessionTaskDelegat
4447
/// The raw URLSession being used for this client
4548
open private(set) var session: URLSession!
4649

50+
private var hasBeenInvalidated = Atomic<Bool>(false)
51+
52+
private var hasNotBeenInvalidated: Bool {
53+
!self.hasBeenInvalidated.value
54+
}
55+
4756
/// Designated initializer.
4857
///
4958
/// - Parameters:
@@ -61,6 +70,7 @@ open class URLSessionClient: NSObject, URLSessionDelegate, URLSessionTaskDelegat
6170
///
6271
/// NOTE: This must be called from the `deinit` of anything holding onto this client in order to break a retain cycle with the delegate.
6372
public func invalidate() {
73+
self.hasBeenInvalidated.value = true
6474
func cleanup() {
6575
self.session = nil
6676
self.clearAllTasks()
@@ -107,6 +117,11 @@ open class URLSessionClient: NSObject, URLSessionDelegate, URLSessionTaskDelegat
107117
open func sendRequest(_ request: URLRequest,
108118
rawTaskCompletionHandler: RawCompletion? = nil,
109119
completion: @escaping Completion) -> URLSessionTask {
120+
guard self.hasNotBeenInvalidated else {
121+
completion(.failure(URLSessionClientError.sessionInvalidated))
122+
return URLSessionTask()
123+
}
124+
110125
let task = self.session.dataTask(with: request)
111126
let taskData = TaskData(rawCompletion: rawTaskCompletionHandler,
112127
completionBlock: completion)

Tests/ApolloTests/URLSessionClientTests.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,4 +214,31 @@ class URLSessionClientLiveTests: XCTestCase {
214214
let set = Set(taskIDs.value)
215215
XCTAssertEqual(set.count, iterations)
216216
}
217+
218+
func testInvalidatingClientAndThenTryingToSendARequestReturnsAppropriateError() {
219+
let client = URLSessionClient()
220+
client.invalidate()
221+
222+
let expectation = self.expectation(description: "Basic GET request completed")
223+
client.sendRequest(self.request(for: .get)) { result in
224+
defer {
225+
expectation.fulfill()
226+
}
227+
228+
switch result {
229+
case .failure(let error):
230+
switch error {
231+
case URLSessionClient.URLSessionClientError.sessionInvalidated:
232+
// This is what we want
233+
break
234+
default:
235+
XCTFail("Unexpected error: \(error)")
236+
}
237+
case .success:
238+
XCTFail("This should not have succeeded")
239+
}
240+
}
241+
242+
self.wait(for: [expectation], timeout: 10)
243+
}
217244
}

0 commit comments

Comments
 (0)