From 61e01532a99efc37b7ce0d976502541b3da60a97 Mon Sep 17 00:00:00 2001 From: Jeff Johnston Date: Fri, 8 Mar 2024 13:03:28 -0800 Subject: [PATCH 1/4] Session and task descriptions (#286) --- .../MockURLSession.swift | 1 + .../Network/URLSessionClientTests.swift | 45 +++++++++++++++++++ .../Sources/Apollo/URLSessionClient.swift | 17 +++++-- 3 files changed, 59 insertions(+), 4 deletions(-) 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 801b388df..d28c79ce7 100644 --- a/Tests/ApolloTests/Network/URLSessionClientTests.swift +++ b/Tests/ApolloTests/Network/URLSessionClientTests.swift @@ -291,6 +291,51 @@ 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() + } + + 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 ba9e6ce3a..692a65efc 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. @@ -112,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 { @@ -126,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) From 546bc904a9af57d135353570f12e4bb8183d950f Mon Sep 17 00:00:00 2001 From: Anthony Miller Date: Fri, 8 Mar 2024 13:29:24 -0800 Subject: [PATCH 2/4] Add SQLite.swift to Tuist project --- .package.resolved | 24 +++++++++---------- Project.swift | 1 + .../Targets/Target+ApolloTests.swift | 3 +++ 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/.package.resolved b/.package.resolved index 9297504fd..8f2405f5a 100644 --- a/.package.resolved +++ b/.package.resolved @@ -5,8 +5,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/mattgallagher/CwlCatchException.git", "state" : { - "revision" : "3b123999de19bf04905bc1dfdb76f817b0f2cc00", - "version" : "2.1.2" + "revision" : "3ef6999c73b6938cc0da422f2c912d0158abb0a0", + "version" : "2.2.0" } }, { @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/mattgallagher/CwlPreconditionTesting.git", "state" : { - "revision" : "dc9af4781f2afdd1e68e90f80b8603be73ea7abc", - "version" : "2.2.0" + "revision" : "2ef56b2caf25f55fa7eef8784c30d5a767550f54", + "version" : "2.2.1" } }, { @@ -32,8 +32,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/Quick/Nimble.git", "state" : { - "revision" : "c1f3dd66222d5e7a1a20afc237f7e7bc432c564f", - "version" : "13.2.0" + "revision" : "efe11bbca024b57115260709b5c05e01131470d0", + "version" : "13.2.1" } }, { @@ -41,8 +41,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/stephencelis/SQLite.swift.git", "state" : { - "revision" : "7a2e3cd27de56f6d396e84f63beefd0267b55ccb", - "version" : "0.14.1" + "revision" : "e78ae0220e17525a15ac68c697a155eb7a672a8e", + "version" : "0.15.0" } }, { @@ -50,8 +50,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-argument-parser.git", "state" : { - "revision" : "8f4d2753f0e4778c76d5f05ad16c74f707390531", - "version" : "1.2.3" + "revision" : "c8ed701b513cf5177118a175d85fbbbcd707ab41", + "version" : "1.3.0" } }, { @@ -59,8 +59,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-collections", "state" : { - "revision" : "d029d9d39c87bed85b1c50adee7c41795261a192", - "version" : "1.0.6" + "revision" : "94cf62b3ba8d4bed62680a282d4c25f9c63c2efb", + "version" : "1.1.0" } } ], diff --git a/Project.swift b/Project.swift index c7b3e272c..2dd194e55 100644 --- a/Project.swift +++ b/Project.swift @@ -8,6 +8,7 @@ let project = Project( organizationName: "apollographql", packages: [ .package(url: "https://github.com/Quick/Nimble.git", from: "13.2.0"), + .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.13.1"), .package(path: "apollo-ios"), .package(path: "apollo-ios-codegen"), .package(path: "apollo-ios-pagination"), diff --git a/Tuist/ProjectDescriptionHelpers/Targets/Target+ApolloTests.swift b/Tuist/ProjectDescriptionHelpers/Targets/Target+ApolloTests.swift index add5da31c..4edb44789 100644 --- a/Tuist/ProjectDescriptionHelpers/Targets/Target+ApolloTests.swift +++ b/Tuist/ProjectDescriptionHelpers/Targets/Target+ApolloTests.swift @@ -20,7 +20,10 @@ extension Target { .target(name: ApolloTarget.starWarsAPI.name), .target(name: ApolloTarget.uploadAPI.name), .package(product: "Apollo"), + .package(product: "ApolloSQLite"), + .package(product: "ApolloWebSocket"), .package(product: "ApolloTestSupport"), + .package(product: "SQLite"), .package(product: "Nimble") ], settings: .forTarget(target) From 40e4e61525424d8b3fd0013b7d8b3dd7ed1e312b Mon Sep 17 00:00:00 2001 From: Anthony Miller Date: Fri, 8 Mar 2024 13:31:03 -0800 Subject: [PATCH 3/4] Fix backwards compatability of PR #286 --- apollo-ios/Sources/Apollo/URLSessionClient.swift | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/apollo-ios/Sources/Apollo/URLSessionClient.swift b/apollo-ios/Sources/Apollo/URLSessionClient.swift index 692a65efc..a77a2f28f 100644 --- a/apollo-ios/Sources/Apollo/URLSessionClient.swift +++ b/apollo-ios/Sources/Apollo/URLSessionClient.swift @@ -144,7 +144,19 @@ open class URLSessionClient: NSObject, URLSessionDelegate, URLSessionTaskDelegat return task } - + + @discardableResult + open func sendRequest(_ request: URLRequest, + rawTaskCompletionHandler: RawCompletion? = nil, + completion: @escaping Completion) -> URLSessionTask { + sendRequest( + request, + taskDescription: nil, + rawTaskCompletionHandler: nil, + completion: completion + ) + } + /// Cancels a given task and clears out its underlying data. /// /// NOTE: You will not receive any kind of "This was cancelled" error when this is called. From 04dc65e50c0d7748a9a0d6c5b9ceebbca63b8085 Mon Sep 17 00:00:00 2001 From: Anthony Miller Date: Fri, 8 Mar 2024 13:35:36 -0800 Subject: [PATCH 4/4] Fix backwards compatibility of contextIdentifier on ApolloClientProtocol --- .../Sources/Apollo/ApolloClientProtocol.swift | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/apollo-ios/Sources/Apollo/ApolloClientProtocol.swift b/apollo-ios/Sources/Apollo/ApolloClientProtocol.swift index 5c6180838..deb877ec3 100644 --- a/apollo-ios/Sources/Apollo/ApolloClientProtocol.swift +++ b/apollo-ios/Sources/Apollo/ApolloClientProtocol.swift @@ -95,3 +95,60 @@ public protocol ApolloClientProtocol: AnyObject { queue: DispatchQueue, resultHandler: @escaping GraphQLResultHandler) -> Cancellable } + +// MARK: - Backwards Compatibilty Extension + +public extension ApolloClientProtocol { + + /// Fetches a query from the server or from the local cache, depending on the current contents of the cache and the specified cache policy. + /// + /// - Parameters: + /// - query: The query to fetch. + /// - cachePolicy: A cache policy that specifies when results should be fetched from the server and when data should be loaded from the local cache. + /// - queue: A dispatch queue on which the result handler will be called. Should default to the main queue. + /// - context: [optional] A context that is being passed through the request chain. Should default to `nil`. + /// - resultHandler: [optional] A closure that is called when query results are available or when an error occurs. + /// - Returns: An object that can be used to cancel an in progress fetch. + func fetch( + query: Query, + cachePolicy: CachePolicy, + context: RequestContext?, + queue: DispatchQueue, + resultHandler: GraphQLResultHandler? + ) -> Cancellable { + self.fetch( + query: query, + cachePolicy: cachePolicy, + contextIdentifier: nil, + context: context, + queue: queue, + resultHandler: resultHandler + ) + } + + /// Performs a mutation by sending it to the server. + /// + /// - Parameters: + /// - mutation: The mutation to perform. + /// - publishResultToStore: If `true`, this will publish the result returned from the operation to the cache store. Default is `true`. + /// - context: [optional] A context that is being passed through the request chain. Should default to `nil`. + /// - queue: A dispatch queue on which the result handler will be called. Should default to the main queue. + /// - resultHandler: An optional closure that is called when mutation results are available or when an error occurs. + /// - Returns: An object that can be used to cancel an in progress mutation. + func perform( + mutation: Mutation, + publishResultToStore: Bool, + context: RequestContext?, + queue: DispatchQueue, + resultHandler: GraphQLResultHandler? + ) -> Cancellable { + self.perform( + mutation: mutation, + publishResultToStore: publishResultToStore, + contextIdentifier: nil, + context: context, + queue: queue, + resultHandler: resultHandler + ) + } +}