import Foundation /// Token 中枢:读写 Keychain + 串行刷新(对应安卓 RefreshAuthenticator 的 @Synchronized 去重)。 /// actor 保证并发安全;`inFlight` 让同时撞 401 的多个请求复用同一次刷新,只刷一次。 actor AuthTokens { static let shared = AuthTokens() private var inFlight: Task? func accessToken() -> String? { KeychainStore.get(.accessToken) } func refreshToken() -> String? { KeychainStore.get(.refreshToken) } func setTokens(access: String, refresh: String) { KeychainStore.set(access, .accessToken) KeychainStore.set(refresh, .refreshToken) } func clear() { KeychainStore.delete(.accessToken) KeychainStore.delete(.refreshToken) } /// 用 refresh_token 换新一对。并发调用复用同一 Task。成功写回 Keychain 返 true。 func refresh() async -> Bool { if let inFlight { return await inFlight.value } let task = Task { await Self.performRefresh() } inFlight = task let ok = await task.value inFlight = nil return ok } /// 裸刷新:不带鉴权头、不会再触发刷新(避免递归),对应安卓"用裸 OkHttp 调 /refresh"。 private static func performRefresh() async -> Bool { guard let rt = KeychainStore.get(.refreshToken) else { return false } do { let pair = try await APIClient.shared.rawRefresh(refreshToken: rt) KeychainStore.set(pair.accessToken, .accessToken) KeychainStore.set(pair.refreshToken, .refreshToken) return true } catch { return false } } }