Skip to content

Commit b067550

Browse files
fix: Interceptor 로직 개선
여러 API를 동시에 호출할 때 토큰 갱신 로직이 중복 호출될 수 있는 문제를 개선했습니다. 스레드 안정성을 개선했습니다.
1 parent db990c3 commit b067550

1 file changed

Lines changed: 54 additions & 8 deletions

File tree

Koin/Data/Service/Network/Interceptor.swift

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,33 +14,79 @@ final class Interceptor: RequestInterceptor {
1414
private let retryLimit = 1
1515
private var subscriptions: Set<AnyCancellable> = []
1616

17+
private let lock = NSLock()
18+
@Published private var isRefreshing = false
19+
1720
func adapt(_ urlRequest: URLRequest,
1821
for session: Session,
1922
completion: @escaping (Result<URLRequest, any Error>) -> Void) {
2023

21-
var urlRequest = urlRequest
22-
if let token = KeychainWorker.shared.read(key: .access) {
23-
urlRequest.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
24+
lock.lock()
25+
defer {
26+
lock.unlock()
27+
}
28+
29+
switch isRefreshing {
30+
case true:
31+
$isRefreshing
32+
.filter { $0 == false }
33+
.first()
34+
.sink { _ in
35+
var urlRequest = urlRequest
36+
if let token = KeychainWorker.shared.read(key: .access) {
37+
urlRequest.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
38+
}
39+
completion(.success(urlRequest))
40+
}
41+
.store(in: &subscriptions)
42+
case false:
43+
var urlRequest = urlRequest
44+
if let token = KeychainWorker.shared.read(key: .access) {
45+
urlRequest.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
46+
}
47+
completion(.success(urlRequest))
2448
}
25-
completion(.success(urlRequest))
2649
}
2750

2851
func retry(_ request: Request,
2952
for session: Session,
3053
dueTo error: any Error,
3154
completion: @escaping @Sendable (RetryResult) -> Void) {
3255

33-
guard let responseCode = error.asAFError?.responseCode,
56+
guard let responseCode = request.response?.statusCode,
3457
responseCode == 401,
3558
request.retryCount < retryLimit,
3659
let _ = KeychainWorker.shared.read(key: .refresh) else {
3760
completion(.doNotRetryWithError(error))
3861
return
3962
}
4063

41-
refreshToken().sink( receiveValue: { isRefreshed in
42-
completion(isRefreshed ? .retry : .doNotRetryWithError(error))
43-
}).store(in: &subscriptions)
64+
lock.lock()
65+
defer {
66+
lock.unlock()
67+
}
68+
69+
switch isRefreshing {
70+
case true:
71+
$isRefreshing
72+
.filter { $0 == false }
73+
.first()
74+
.sink { _ in
75+
completion(.retry)
76+
}
77+
.store(in: &subscriptions)
78+
case false:
79+
isRefreshing = true
80+
refreshToken().sink( receiveValue: { [weak self] isRefreshed in
81+
guard let self else { return }
82+
83+
lock.lock()
84+
isRefreshing = false
85+
lock.unlock()
86+
87+
completion(isRefreshed ? .retry : .doNotRetryWithError(error))
88+
}).store(in: &subscriptions)
89+
}
4490
}
4591
}
4692

0 commit comments

Comments
 (0)