diff --git a/Sources/munkit/core/api-target/APITarget.swift b/Sources/munkit/core/api-target/APITarget.swift index 3f268f0..e7b4b3f 100644 --- a/Sources/munkit/core/api-target/APITarget.swift +++ b/Sources/munkit/core/api-target/APITarget.swift @@ -9,7 +9,7 @@ import Moya import Foundation public protocol MUNAPITarget: TargetType, AccessTokenAuthorizable, Sendable { - var parameters: [String: Any] { get } + var parameters: [String: Sendable] { get } var isAccessTokenRequired: Bool { get } var isRefreshTokenRequest: Bool { get } var isMockEnabled: Bool { get } diff --git a/Sources/munkit/core/network-service/NetworkService.swift b/Sources/munkit/core/network-service/NetworkService.swift index f2d4ca9..e835f3b 100644 --- a/Sources/munkit/core/network-service/NetworkService.swift +++ b/Sources/munkit/core/network-service/NetworkService.swift @@ -43,14 +43,82 @@ public actor MUNNetworkService { plugins: moyaProvider.plugins + [accessTokenPlugin] ) } + + public func cancelRequests() { + tokenRefreshTask?.cancel() + tokenRefreshTask = nil + + activeRequests.forEach { request in + request.task.cancel() + } + + activeRequests.removeAll() + requestsPendingTokenRefresh.removeAll() + + _Concurrency.Task { @MUNLogger in + MUNLogger.sharedLoggable?.log(type: .info, "All network requests have been cancelled") + } + } public func executeRequest( target: Target, isTokenRefreshed: Bool = false ) async throws -> T { - let requestId = startRequest(isAccessTokenRequired: target.isAccessTokenRequired) - defer { completeRequest(requestId) } + let requestId = UUID() + let requestTask = _Concurrency.Task { + try await performExecuteRequestWithDecodable( + requestId: requestId, + target: target, + isTokenRefreshed: isTokenRefreshed + ) + } + + saveActiveRequest( + requestId: requestId, + isAccessTokenRequired: target.isAccessTokenRequired, + task: requestTask + ) + + do { + let result = try await requestTask.value + completeRequest(requestId) + return result + } catch { + completeRequest(requestId) + throw error + } + } + + public func executeRequest(target: Target, isTokenRefreshed: Bool = false) async throws { + let requestId = UUID() + let requestTask = _Concurrency.Task { + try await performExecuteRequestWithoutDecodable( + requestId: requestId, + target: target, + isTokenRefreshed: isTokenRefreshed + ) + } + + saveActiveRequest( + requestId: requestId, + isAccessTokenRequired: target.isAccessTokenRequired, + task: requestTask + ) + + do { + try await requestTask.value + completeRequest(requestId) + } catch { + completeRequest(requestId) + throw error + } + } + private func performExecuteRequestWithDecodable( + requestId: UUID, + target: Target, + isTokenRefreshed: Bool + ) async throws -> T { do { let response = try await performRequest(target: target).get() let filteredResponse = try response.filterSuccessfulStatusCodes() @@ -62,14 +130,19 @@ public actor MUNNetworkService { target: target, isTokenRefreshed: isTokenRefreshed ) - return try await executeRequest(target: target, isTokenRefreshed: true) + return try await performExecuteRequestWithDecodable( + requestId: requestId, + target: target, + isTokenRefreshed: true + ) } } - public func executeRequest(target: Target, isTokenRefreshed: Bool = false) async throws { - let requestId = startRequest(isAccessTokenRequired: target.isAccessTokenRequired) - defer { completeRequest(requestId) } - + private func performExecuteRequestWithoutDecodable( + requestId: UUID, + target: Target, + isTokenRefreshed: Bool + ) async throws { do { let response = try await performRequest(target: target).get() let _ = try response.filterSuccessfulStatusCodes() @@ -80,16 +153,26 @@ public actor MUNNetworkService { target: target, isTokenRefreshed: isTokenRefreshed ) - try await executeRequest(target: target, isTokenRefreshed: true) + try await performExecuteRequestWithoutDecodable( + requestId: requestId, + target: target, + isTokenRefreshed: true + ) } } - private func startRequest(isAccessTokenRequired: Bool) -> UUID { - let requestId = UUID() + private func saveActiveRequest( + requestId: UUID, + isAccessTokenRequired: Bool, + task: _Concurrency.Task + ) { activeRequests.insert( - NetworkServiceActiveRequest(id: requestId, isAccessTokenRequired: isAccessTokenRequired) + NetworkServiceActiveRequest( + id: requestId, + isAccessTokenRequired: isAccessTokenRequired, + task: .init(task) + ) ) - return requestId } private func performRequest(target: Target) async throws -> Result { diff --git a/Sources/munkit/core/network-service/NetworkServiceActiveRequest.swift b/Sources/munkit/core/network-service/NetworkServiceActiveRequest.swift index 9c97f3a..c28f015 100644 --- a/Sources/munkit/core/network-service/NetworkServiceActiveRequest.swift +++ b/Sources/munkit/core/network-service/NetworkServiceActiveRequest.swift @@ -8,10 +8,28 @@ import Foundation struct NetworkServiceActiveRequest: Hashable { + struct AnyTask { + private let _cancel: () -> Void + + init(_ task: Task) { + _cancel = { task.cancel() } + } + + func cancel() { + _cancel() + } + } + let id: UUID let isAccessTokenRequired: Bool + let task: AnyTask + + static func == (lhs: Self, rhs: Self) -> Bool { + return lhs.hashValue == rhs.hashValue + } func hash(into hasher: inout Hasher) { hasher.combine(id) + hasher.combine(isAccessTokenRequired) } }