
主要变更: 1. 新增 EPImageUploader.swift 和 EPProgressHUD.swift,提供图片批量上传和进度显示功能。 2. 新建 EPMomentAPISwiftHelper.swift,封装动态 API 的 Swift 版本。 3. 更新 EPMomentPublishViewController,集成新上传功能并实现发布成功通知。 4. 创建多个文档,包括实施报告、检查清单和快速使用指南,详细记录功能实现和使用方法。 5. 更新 Bridging Header,确保 Swift 和 Objective-C 代码的互操作性。 此功能旨在提升用户体验,简化动态发布流程,并提供清晰的文档支持。
254 lines
8.4 KiB
Swift
254 lines
8.4 KiB
Swift
//
|
||
// EPSDKManager.swift
|
||
// YuMi
|
||
//
|
||
// Created by AI on 2025-10-11.
|
||
//
|
||
|
||
import Foundation
|
||
|
||
/// 第三方 SDK 统一管理器(单例)
|
||
/// 统一入口:对外提供所有 SDK 能力
|
||
/// 内部管理:QCloud 初始化、配置、上传等
|
||
@objc class EPSDKManager: NSObject, QCloudSignatureProvider, QCloudCredentailFenceQueueDelegate {
|
||
|
||
// MARK: - Singleton
|
||
|
||
@objc static let shared = EPSDKManager()
|
||
|
||
// MARK: - Properties
|
||
|
||
// QCloud 配置缓存
|
||
private var qcloudConfig: EPQCloudConfig?
|
||
|
||
// QCloud 初始化状态
|
||
private var isQCloudInitializing = false
|
||
|
||
// QCloud 初始化回调队列
|
||
private var qcloudInitCallbacks: [(Bool, String?) -> Void] = []
|
||
|
||
// QCloud 凭证队列
|
||
private var credentialFenceQueue: QCloudCredentailFenceQueue?
|
||
|
||
// 线程安全锁
|
||
private let lock = NSLock()
|
||
|
||
// 内部图片上传器
|
||
private let uploader = EPImageUploader()
|
||
|
||
// MARK: - Initialization
|
||
|
||
private override init() {
|
||
super.init()
|
||
}
|
||
|
||
// MARK: - Public API (对外统一入口)
|
||
|
||
/// 批量上传图片(统一入口)
|
||
/// - Parameters:
|
||
/// - images: 要上传的图片数组
|
||
/// - progress: 进度回调 (已上传数, 总数)
|
||
/// - success: 成功回调,返回图片信息数组
|
||
/// - failure: 失败回调
|
||
@objc func uploadImages(
|
||
_ images: [UIImage],
|
||
progress: @escaping (Int, Int) -> Void,
|
||
success: @escaping ([[String: Any]]) -> Void,
|
||
failure: @escaping (String) -> Void
|
||
) {
|
||
guard !images.isEmpty else {
|
||
success([])
|
||
return
|
||
}
|
||
|
||
// 确保 QCloud 已就绪
|
||
ensureQCloudReady { [weak self] isReady, errorMsg in
|
||
guard let self = self, isReady else {
|
||
DispatchQueue.main.async {
|
||
failure(errorMsg ?? "QCloud 初始化失败")
|
||
}
|
||
return
|
||
}
|
||
|
||
// 委托给内部 uploader 执行
|
||
self.uploader.performBatchUpload(
|
||
images,
|
||
bucket: self.qcloudConfig?.bucket ?? "",
|
||
customDomain: self.qcloudConfig?.customDomain ?? "",
|
||
progress: progress,
|
||
success: success,
|
||
failure: failure
|
||
)
|
||
}
|
||
}
|
||
|
||
/// 检查 QCloud 是否已就绪
|
||
/// - Returns: true 表示已初始化且未过期
|
||
@objc func isQCloudReady() -> Bool {
|
||
lock.lock()
|
||
defer { lock.unlock() }
|
||
|
||
guard let config = qcloudConfig else {
|
||
return false
|
||
}
|
||
return !config.isExpired
|
||
}
|
||
|
||
// MARK: - Internal Methods
|
||
|
||
/// 确保 QCloud 已就绪(自动初始化)
|
||
private func ensureQCloudReady(completion: @escaping (Bool, String?) -> Void) {
|
||
if isQCloudReady() {
|
||
completion(true, nil)
|
||
return
|
||
}
|
||
|
||
// 未初始化或已过期,重新初始化
|
||
initializeQCloud(completion: completion)
|
||
}
|
||
|
||
/// 初始化 QCloud(获取 Token 并配置 SDK)
|
||
private func initializeQCloud(completion: @escaping (Bool, String?) -> Void) {
|
||
lock.lock()
|
||
|
||
// 如果正在初始化,加入回调队列
|
||
if isQCloudInitializing {
|
||
qcloudInitCallbacks.append(completion)
|
||
lock.unlock()
|
||
return
|
||
}
|
||
|
||
// 如果已初始化且未过期,直接返回
|
||
if let config = qcloudConfig, !config.isExpired {
|
||
lock.unlock()
|
||
completion(true, nil)
|
||
return
|
||
}
|
||
|
||
// 开始初始化
|
||
isQCloudInitializing = true
|
||
qcloudInitCallbacks.append(completion)
|
||
lock.unlock()
|
||
|
||
// 调用 API 获取 QCloud Token
|
||
// API: GET tencent/cos/getToken
|
||
Api.getQCloudInfo { [weak self] (data, code, msg) in
|
||
guard let self = self else { return }
|
||
|
||
self.lock.lock()
|
||
|
||
if code == 200,
|
||
let dict = data?.data as? [String: Any],
|
||
let config = EPQCloudConfig(dictionary: dict) {
|
||
|
||
// 保存配置
|
||
self.qcloudConfig = config
|
||
|
||
// 配置 QCloud SDK
|
||
self.configureQCloudSDK(with: config)
|
||
|
||
// 初始化完成
|
||
self.isQCloudInitializing = false
|
||
let callbacks = self.qcloudInitCallbacks
|
||
self.qcloudInitCallbacks.removeAll()
|
||
self.lock.unlock()
|
||
|
||
// 短暂延迟确保 SDK 配置完成
|
||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
|
||
callbacks.forEach { $0(true, nil) }
|
||
}
|
||
} else {
|
||
// 初始化失败
|
||
self.isQCloudInitializing = false
|
||
let callbacks = self.qcloudInitCallbacks
|
||
self.qcloudInitCallbacks.removeAll()
|
||
self.lock.unlock()
|
||
|
||
let errorMsg = msg ?? "获取 QCloud 配置失败"
|
||
DispatchQueue.main.async {
|
||
callbacks.forEach { $0(false, errorMsg) }
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 配置 QCloud SDK(参考 UploadFile.m line 42-64)
|
||
private func configureQCloudSDK(with config: EPQCloudConfig) {
|
||
let configuration = QCloudServiceConfiguration()
|
||
configuration.appID = config.appId
|
||
|
||
let endpoint = QCloudCOSXMLEndPoint()
|
||
endpoint.regionName = config.region
|
||
endpoint.useHTTPS = true
|
||
|
||
// 全球加速(参考 UploadFile.m line 56-59)
|
||
if config.accelerate == 1 {
|
||
endpoint.suffix = "cos.accelerate.myqcloud.com"
|
||
}
|
||
|
||
configuration.endpoint = endpoint
|
||
configuration.signatureProvider = self
|
||
|
||
// 注册 COS 服务
|
||
QCloudCOSXMLService.registerDefaultCOSXML(with: configuration)
|
||
QCloudCOSTransferMangerService.registerDefaultCOSTransferManger(with: configuration)
|
||
|
||
// 初始化凭证队列
|
||
credentialFenceQueue = QCloudCredentailFenceQueue()
|
||
credentialFenceQueue?.delegate = self
|
||
}
|
||
|
||
// MARK: - QCloudSignatureProvider Protocol
|
||
|
||
/// 提供签名(参考 UploadFile.m line 67-104)
|
||
func signature(
|
||
with fields: QCloudSignatureFields,
|
||
request: QCloudBizHTTPRequest,
|
||
urlRequest: NSMutableURLRequest,
|
||
compelete: @escaping QCloudHTTPAuthentationContinueBlock
|
||
) {
|
||
guard let config = qcloudConfig else {
|
||
let error = NSError(domain: "com.yumi.qcloud", code: -1,
|
||
userInfo: [NSLocalizedDescriptionKey: "QCloud 配置未初始化"])
|
||
compelete(nil, error)
|
||
return
|
||
}
|
||
|
||
let credential = QCloudCredential()
|
||
credential.secretID = config.secretId
|
||
credential.secretKey = config.secretKey
|
||
credential.token = config.sessionToken
|
||
credential.startDate = Date(timeIntervalSince1970: TimeInterval(config.startTime))
|
||
credential.expirationDate = Date(timeIntervalSince1970: TimeInterval(config.expireTime))
|
||
|
||
let creator = QCloudAuthentationV5Creator(credential: credential)
|
||
let signature = creator?.signature(forData: urlRequest)
|
||
compelete(signature, nil)
|
||
}
|
||
|
||
// MARK: - QCloudCredentailFenceQueueDelegate Protocol
|
||
|
||
/// 管理凭证(参考 UploadFile.m line 107-133)
|
||
func fenceQueue(
|
||
_ queue: QCloudCredentailFenceQueue,
|
||
requestCreatorWithContinue continueBlock: @escaping QCloudCredentailFenceQueueContinue
|
||
) {
|
||
guard let config = qcloudConfig else {
|
||
let error = NSError(domain: "com.yumi.qcloud", code: -1,
|
||
userInfo: [NSLocalizedDescriptionKey: "QCloud 配置未初始化"])
|
||
continueBlock(nil, error)
|
||
return
|
||
}
|
||
|
||
let credential = QCloudCredential()
|
||
credential.secretID = config.secretId
|
||
credential.secretKey = config.secretKey
|
||
credential.token = config.sessionToken
|
||
credential.startDate = Date(timeIntervalSince1970: TimeInterval(config.startTime))
|
||
credential.expirationDate = Date(timeIntervalSince1970: TimeInterval(config.expireTime))
|
||
|
||
let creator = QCloudAuthentationV5Creator(credential: credential)
|
||
continueBlock(creator, nil)
|
||
}
|
||
}
|