From 856d1fc60d554e779d5ac96981f766a5866155a5 Mon Sep 17 00:00:00 2001 From: Dominika Gajdova Date: Mon, 3 Feb 2025 15:58:53 +0100 Subject: [PATCH] chore: add protocol to unite url session specific invalidation functionality --- Sources/Networking/Core/APIManager.swift | 27 +++++++++++++------ Sources/Networking/Core/APIManaging.swift | 10 ++----- .../Networking/Core/ResponseProviding.swift | 11 -------- .../Core/URLSessionInvalidatable.swift | 19 +++++++++++++ 4 files changed, 40 insertions(+), 27 deletions(-) create mode 100644 Sources/Networking/Core/URLSessionInvalidatable.swift diff --git a/Sources/Networking/Core/APIManager.swift b/Sources/Networking/Core/APIManager.swift index 1b90ad7f..8e287134 100644 --- a/Sources/Networking/Core/APIManager.swift +++ b/Sources/Networking/Core/APIManager.swift @@ -50,7 +50,8 @@ open class APIManager: APIManaging, Retryable { private let responseProcessors: [ResponseProcessing] private let errorProcessors: [ErrorProcessing] private let sessionId: String - private var _responseProvider: ResponseProviding + private var responseProvider: ResponseProviding + private var _urlSessionIsInvalidated: Bool = false internal var retryCounter = Counter() @@ -69,7 +70,7 @@ open class APIManager: APIManaging, Retryable { sessionId = Date().ISO8601Format() } - self._responseProvider = urlSession + self.responseProvider = urlSession self.requestAdapters = requestAdapters self.responseProcessors = responseProcessors self.errorProcessors = errorProcessors @@ -89,7 +90,7 @@ open class APIManager: APIManaging, Retryable { } else { sessionId = Date().ISO8601Format() } - self._responseProvider = responseProvider + self.responseProvider = responseProvider self.requestAdapters = requestAdapters self.responseProcessors = responseProcessors self.errorProcessors = errorProcessors @@ -103,15 +104,25 @@ open class APIManager: APIManaging, Retryable { } } -// MARK: Response provider +// MARK: URL Session Invalidation public extension APIManager { - var responseProvider: ResponseProviding { - _responseProvider + var urlSessionIsInvalidated: Bool { + _urlSessionIsInvalidated } - func setResponseProvider(_ provider: ResponseProviding) { - self._responseProvider = provider + var urlSession: URLSession? { + responseProvider as? URLSession + } + + func setUrlSession(_ urlSession: URLSession) { + responseProvider = urlSession + } + + func invalidateUrlSession() async { + await urlSession?.allTasks.forEach { $0.cancel() } + urlSession?.invalidateAndCancel() + _urlSessionIsInvalidated = true } } diff --git a/Sources/Networking/Core/APIManaging.swift b/Sources/Networking/Core/APIManaging.swift index aa4e5d34..ce7b0c5f 100644 --- a/Sources/Networking/Core/APIManaging.swift +++ b/Sources/Networking/Core/APIManaging.swift @@ -11,7 +11,7 @@ import Foundation // MARK: - Defines API managing /// A definition of an API layer with methods for handling API requests. -public protocol APIManaging { +public protocol APIManaging: URLSessionInvalidatable { /// A default `JSONDecoder` used for all requests. var defaultDecoder: JSONDecoder { get } @@ -33,13 +33,7 @@ public protocol APIManaging { _ endpoint: Requestable, decoder: JSONDecoder, retryConfiguration: RetryConfiguration? - ) async throws -> DecodableResponse - - /// Invalidates and response provider to gracefully clear out all its tasks. - var responseProvider: ResponseProviding { get } - - /// Replaces the response provider instance used by APIManager. - func setResponseProvider(_ provider: ResponseProviding) + ) async throws -> DecodableResponse } // MARK: - Provide request with default json decoder, retry configuration diff --git a/Sources/Networking/Core/ResponseProviding.swift b/Sources/Networking/Core/ResponseProviding.swift index d4adf06f..f6a374bb 100644 --- a/Sources/Networking/Core/ResponseProviding.swift +++ b/Sources/Networking/Core/ResponseProviding.swift @@ -13,9 +13,6 @@ import Foundation public protocol ResponseProviding { /// Creates a ``Response`` for a given `URLRequest`. func response(for request: URLRequest) async throws -> Response - - /// Invalidates and response provider to gracefully clear out all its tasks. - func invalidate() async } extension URLSession: ResponseProviding { @@ -23,12 +20,4 @@ extension URLSession: ResponseProviding { public func response(for request: URLRequest) async throws -> Response { try await data(for: request) } - - /// Invalidates and response provider to gracefully clear out all its tasks. - /// Warning: URLSession can no longer be used after it's been invalidated and any usage - /// will lead to a crash. - public func invalidate() async { - await allTasks.forEach { $0.cancel() } - invalidateAndCancel() - } } diff --git a/Sources/Networking/Core/URLSessionInvalidatable.swift b/Sources/Networking/Core/URLSessionInvalidatable.swift new file mode 100644 index 00000000..7373505c --- /dev/null +++ b/Sources/Networking/Core/URLSessionInvalidatable.swift @@ -0,0 +1,19 @@ +import Foundation + +/// Adds capability for `URLSession` to be invalidated and recreated. +public protocol URLSessionInvalidatable { + /// Invalidates urlSession to gracefully clear out all its tasks. + var urlSession: URLSession? { get } + + /// Returns `true` if session has been invalidate and is no longer suitable for usage. + /// Any other usage of this urlSession will lead to runtime error. + var urlSessionIsInvalidated: Bool { get } + + /// Replaces the urlSession instance used by APIManager. + func setUrlSession(_ urlSession: URLSession) + + /// Invalidates the current urlSession. + /// Warning: urlSession must be recreated before further usage + /// otherwise runtime error is encountered as accessing invalidated session is illegal. + func invalidateUrlSession() async +}