From a66a4bc62e8d481880aac8523ecb97d7a19dd298 Mon Sep 17 00:00:00 2001 From: Jeff Johnston Date: Sun, 17 Dec 2023 08:44:46 -0800 Subject: [PATCH 1/2] Add a sessionDescription to URLSessionClient Provides an optional `sessionDescription` to the `URLSessionClient` initializer. If provided this will be added to the underlying `URLSession` to aid in debugging. See: https://developer.apple.com/documentation/foundation/urlsession/1408277-sessiondescription --- .../ApolloTests/Network/URLSessionClientTests.swift | 12 ++++++++++++ apollo-ios/Sources/Apollo/URLSessionClient.swift | 13 +++++++++---- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/Tests/ApolloTests/Network/URLSessionClientTests.swift b/Tests/ApolloTests/Network/URLSessionClientTests.swift index 801b388df..002a8164f 100644 --- a/Tests/ApolloTests/Network/URLSessionClientTests.swift +++ b/Tests/ApolloTests/Network/URLSessionClientTests.swift @@ -291,6 +291,18 @@ class URLSessionClientTests: XCTestCase { self.wait(for: [expectation], timeout: 5) } + func testSessionDescription() { + // Should be nil by default. + XCTAssertNil(client.session.sessionDescription) + + // Should set the sessionDescription of the URLSession. + let expected = "test description" + let client2 = URLSessionClient(sessionConfiguration: sessionConfiguration, + sessionDescription: expected) + XCTAssertEqual(expected, client2.session.sessionDescription) + + client2.invalidate() + } } extension URLSessionClientTests: MockRequestProvider { diff --git a/apollo-ios/Sources/Apollo/URLSessionClient.swift b/apollo-ios/Sources/Apollo/URLSessionClient.swift index ba9e6ce3a..7f636246f 100644 --- a/apollo-ios/Sources/Apollo/URLSessionClient.swift +++ b/apollo-ios/Sources/Apollo/URLSessionClient.swift @@ -61,12 +61,17 @@ open class URLSessionClient: NSObject, URLSessionDelegate, URLSessionTaskDelegat /// - Parameters: /// - sessionConfiguration: The `URLSessionConfiguration` to use to set up the URL session. /// - callbackQueue: [optional] The `OperationQueue` to tell the URL session to call back to this class on, which will in turn call back to your class. Defaults to `.main`. + /// - sessionDescription: [optional] A human-readable string that you can use for debugging purposes. public init(sessionConfiguration: URLSessionConfiguration = .default, - callbackQueue: OperationQueue? = .main) { + callbackQueue: OperationQueue? = .main, + sessionDescription: String? = nil) { super.init() - self.session = URLSession(configuration: sessionConfiguration, - delegate: self, - delegateQueue: callbackQueue) + + let session = URLSession(configuration: sessionConfiguration, + delegate: self, + delegateQueue: callbackQueue) + session.sessionDescription = sessionDescription + self.session = session } /// Cleans up and invalidates everything related to this session client. From ac251ad4f91d3bc8fd39a78ec508ea541c83dd91 Mon Sep 17 00:00:00 2001 From: Jeff Johnston Date: Sun, 17 Dec 2023 09:02:54 -0800 Subject: [PATCH 2/2] Add a taskDescription to sendRequest Provides an optional `taskDescription` to `URLSessionClient.sendRequest(_:_:_:_:)`. If provided this will be added to the returned `URLSessionTask` where it can be used for debugging purposes. See: https://developer.apple.com/documentation/foundation/urlsessiontask/1409798-taskdescription Note: Not to be conflated with the `taskIdentifier`. --- .../MockURLSession.swift | 1 + .../Network/URLSessionClientTests.swift | 33 +++++++++++++++++++ .../Sources/Apollo/URLSessionClient.swift | 4 +++ 3 files changed, 38 insertions(+) diff --git a/Tests/ApolloInternalTestHelpers/MockURLSession.swift b/Tests/ApolloInternalTestHelpers/MockURLSession.swift index 3cff26929..6ff9bb219 100644 --- a/Tests/ApolloInternalTestHelpers/MockURLSession.swift +++ b/Tests/ApolloInternalTestHelpers/MockURLSession.swift @@ -28,6 +28,7 @@ public final class MockURLSessionClient: URLSessionClient { } public override func sendRequest(_ request: URLRequest, + taskDescription: String? = nil, rawTaskCompletionHandler: URLSessionClient.RawCompletion? = nil, completion: @escaping URLSessionClient.Completion) -> URLSessionTask { self.$lastRequest.mutate { $0 = request } diff --git a/Tests/ApolloTests/Network/URLSessionClientTests.swift b/Tests/ApolloTests/Network/URLSessionClientTests.swift index 002a8164f..d28c79ce7 100644 --- a/Tests/ApolloTests/Network/URLSessionClientTests.swift +++ b/Tests/ApolloTests/Network/URLSessionClientTests.swift @@ -303,6 +303,39 @@ class URLSessionClientTests: XCTestCase { client2.invalidate() } + + func testTaskDescription() { + let url = URL(string: "http://www.test.com/taskDesciption")! + + let request = request(for: url, + responseData: nil, + statusCode: -1) + + let expectation = self.expectation(description: "Described task completed") + expectation.isInverted = true + + let task = self.client.sendRequest(request) { result in + // This shouldn't get hit since we cancel the task immediately + expectation.fulfill() + } + self.client.cancel(task: task) + + // Should be nil by default. + XCTAssertNil(task.taskDescription) + + let expected = "test task description" + let describedTask = self.client.sendRequest(request, + taskDescription: expected) { result in + // This shouldn't get hit since we cancel the task immediately + expectation.fulfill() + } + self.client.cancel(task: describedTask) + + // The returned task should have the provided taskDescription. + XCTAssertEqual(expected, describedTask.taskDescription) + + self.wait(for: [expectation], timeout: 5) + } } extension URLSessionClientTests: MockRequestProvider { diff --git a/apollo-ios/Sources/Apollo/URLSessionClient.swift b/apollo-ios/Sources/Apollo/URLSessionClient.swift index 7f636246f..692a65efc 100644 --- a/apollo-ios/Sources/Apollo/URLSessionClient.swift +++ b/apollo-ios/Sources/Apollo/URLSessionClient.swift @@ -117,12 +117,14 @@ open class URLSessionClient: NSObject, URLSessionDelegate, URLSessionTaskDelegat /// /// - Parameters: /// - request: The request to perform. + /// - taskDescription: [optional] A description to add to the `URLSessionTask` for debugging purposes. /// - rawTaskCompletionHandler: [optional] A completion handler to call once the raw task is done, so if an Error requires access to the headers, the user can still access these. /// - completion: A completion handler to call when the task has either completed successfully or failed. /// /// - Returns: The created URLSession task, already resumed, because nobody ever remembers to call `resume()`. @discardableResult open func sendRequest(_ request: URLRequest, + taskDescription: String? = nil, rawTaskCompletionHandler: RawCompletion? = nil, completion: @escaping Completion) -> URLSessionTask { guard self.hasNotBeenInvalidated else { @@ -131,6 +133,8 @@ open class URLSessionClient: NSObject, URLSessionDelegate, URLSessionTaskDelegat } let task = self.session.dataTask(with: request) + task.taskDescription = taskDescription + let taskData = TaskData(rawCompletion: rawTaskCompletionHandler, completionBlock: completion)