Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Sources/munkit/core/api-target/APITarget.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
107 changes: 95 additions & 12 deletions Sources/munkit/core/network-service/NetworkService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,82 @@ public actor MUNNetworkService<Target: MUNAPITarget> {
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<T: Decodable & Sendable>(
target: Target,
isTokenRefreshed: Bool = false
) async throws -> T {
let requestId = startRequest(isAccessTokenRequired: target.isAccessTokenRequired)
defer { completeRequest(requestId) }
let requestId = UUID()
let requestTask = _Concurrency.Task<T, any Error> {
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<Void, any Error> {
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<T: Decodable & Sendable>(
requestId: UUID,
target: Target,
isTokenRefreshed: Bool
) async throws -> T {
do {
let response = try await performRequest(target: target).get()
let filteredResponse = try response.filterSuccessfulStatusCodes()
Expand All @@ -62,14 +130,19 @@ public actor MUNNetworkService<Target: MUNAPITarget> {
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()
Expand All @@ -80,16 +153,26 @@ public actor MUNNetworkService<Target: MUNAPITarget> {
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<T>(
requestId: UUID,
isAccessTokenRequired: Bool,
task: _Concurrency.Task<T, Error>
) {
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<Response, MoyaError> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,28 @@
import Foundation

struct NetworkServiceActiveRequest: Hashable {
struct AnyTask {
private let _cancel: () -> Void

init<T>(_ task: Task<T, Error>) {
_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)
}
}