Files
e-party-iOS/yana/Utils/TCCos/Services/COSTokenService.swift
edwinQQQ b966e24532 feat: 更新COSManager和相关视图以增强图片上传功能
- 修改COSManagerAdapter以支持新的TCCos组件,确保与腾讯云COS的兼容性。
- 在CreateFeedFeature中新增图片上传相关状态和Action,优化图片选择与上传逻辑。
- 更新CreateFeedView以整合图片上传功能,提升用户体验。
- 在多个视图中添加键盘状态管理,改善用户交互体验。
- 新增COS相关的测试文件,确保功能的正确性和稳定性。
2025-07-31 11:41:56 +08:00

218 lines
6.4 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import Foundation
import ComposableArchitecture
// MARK: - Token
/// Token
public protocol COSTokenServiceProtocol: Sendable {
/// Token
func getValidToken() async throws -> TcTokenData
/// Token
func refreshToken() async throws -> TcTokenData
/// Token
func clearCachedToken()
/// Token
func getTokenStatus() async -> String
}
// MARK: - Token
/// Token
public struct COSTokenService: COSTokenServiceProtocol {
private let apiService: any APIServiceProtocol & Sendable
private let tokenCache: TokenCacheProtocol
init(
apiService: any APIServiceProtocol & Sendable,
tokenCache: TokenCacheProtocol = TokenCache()
) {
self.apiService = apiService
self.tokenCache = tokenCache
}
/// Token
public func getValidToken() async throws -> TcTokenData {
//
if let cachedToken = await tokenCache.getValidCachedToken() {
debugInfoSync("🔐 使用缓存的 COS Token")
return cachedToken
}
// Token
debugInfoSync("🔐 开始请求腾讯云 COS Token...")
return try await requestNewToken()
}
/// Token
public func refreshToken() async throws -> TcTokenData {
//
await tokenCache.clearCachedToken()
debugInfoSync("🔄 清除缓存,开始刷新 Token")
// Token
return try await requestNewToken()
}
/// Token
public func clearCachedToken() {
Task {
await tokenCache.clearCachedToken()
debugInfoSync("🗑️ 清除缓存的 COS Token")
}
}
/// Token
public func getTokenStatus() async -> String {
if let cachedToken = await tokenCache.getCachedToken() {
let isExpired = !cachedToken.isValid
return "Token 状态: \(isExpired ? "已过期" : "有效"), 过期时间: \(cachedToken.expirationDate)"
} else {
return "Token 状态: 未缓存"
}
}
// MARK: -
/// Token
private func requestNewToken() async throws -> TcTokenData {
do {
let request = TcTokenRequest()
let response: TcTokenResponse = try await apiService.request(request)
guard response.code == 200, let tokenData = response.data else {
throw COSError.tokenInvalid
}
// Token
await tokenCache.cacheToken(tokenData)
debugInfoSync("✅ COS Token 获取成功")
debugInfoSync(" - 存储桶: \(tokenData.bucket)")
debugInfoSync(" - 地域: \(tokenData.region)")
debugInfoSync(" - 过期时间: \(tokenData.expirationDate)")
debugInfoSync(" - 剩余时间: \(tokenData.remainingTime)")
return tokenData
} catch {
debugErrorSync("❌ COS Token 请求异常: \(error.localizedDescription)")
throw COSError.networkError(error.localizedDescription)
}
}
}
// MARK: - Token
/// Token
public protocol TokenCacheProtocol: Sendable {
/// Token
func getCachedToken() async -> TcTokenData?
/// Token
func getValidCachedToken() async -> TcTokenData?
/// Token
func cacheToken(_ tokenData: TcTokenData) async
/// Token
func clearCachedToken() async
}
// MARK: - Token
/// Token
public actor TokenCache: TokenCacheProtocol {
private var cachedToken: TcTokenData?
private var tokenExpirationDate: Date?
public init() {}
/// Token
public func getCachedToken() async -> TcTokenData? {
return cachedToken
}
/// Token
public func getValidCachedToken() async -> TcTokenData? {
guard let cached = cachedToken,
let expiration = tokenExpirationDate,
Date() < expiration else {
return nil
}
return cached
}
/// Token
public func cacheToken(_ tokenData: TcTokenData) async {
cachedToken = tokenData
tokenExpirationDate = tokenData.expirationDate
debugInfoSync("💾 COS Token 已缓存,过期时间: \(tokenExpirationDate?.description ?? "未知")")
}
/// Token
public func clearCachedToken() async {
cachedToken = nil
tokenExpirationDate = nil
debugInfoSync("🗑️ 清除缓存的 COS Token")
}
}
// MARK: - TCA
extension DependencyValues {
/// COS Token
public var cosTokenService: COSTokenServiceProtocol {
get { self[COSTokenServiceKey.self] }
set { self[COSTokenServiceKey.self] = newValue }
}
}
/// COS Token
private enum COSTokenServiceKey: DependencyKey {
static let liveValue: COSTokenServiceProtocol = COSTokenService(
apiService: LiveAPIService()
)
static let testValue: COSTokenServiceProtocol = MockCOSTokenService()
}
// MARK: - Mock
/// Mock Token
public struct MockCOSTokenService: COSTokenServiceProtocol {
public var getValidTokenResult: Result<TcTokenData, Error> = .failure(COSError.tokenInvalid)
public var refreshTokenResult: Result<TcTokenData, Error> = .failure(COSError.tokenInvalid)
public var tokenStatusResult: String = "Mock Token Status"
public init() {}
public func getValidToken() async throws -> TcTokenData {
switch getValidTokenResult {
case .success(let token):
return token
case .failure(let error):
throw error
}
}
public func refreshToken() async throws -> TcTokenData {
switch refreshTokenResult {
case .success(let token):
return token
case .failure(let error):
throw error
}
}
public func clearCachedToken() {
// Mock
}
public func getTokenStatus() async -> String {
return tokenStatusResult
}
}