feat: 添加动态发布功能及相关文档

主要变更:
1. 新增 EPImageUploader.swift 和 EPProgressHUD.swift,提供图片批量上传和进度显示功能。
2. 新建 EPMomentAPISwiftHelper.swift,封装动态 API 的 Swift 版本。
3. 更新 EPMomentPublishViewController,集成新上传功能并实现发布成功通知。
4. 创建多个文档,包括实施报告、检查清单和快速使用指南,详细记录功能实现和使用方法。
5. 更新 Bridging Header,确保 Swift 和 Objective-C 代码的互操作性。

此功能旨在提升用户体验,简化动态发布流程,并提供清晰的文档支持。
This commit is contained in:
edwinQQQ
2025-10-11 17:16:30 +08:00
parent ceaeb5c951
commit 7626eb8351
29 changed files with 3792 additions and 802 deletions

View File

@@ -0,0 +1,253 @@
//
// 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)
}
}