Swift中多API请求(含视频上传)完成后执行操作的最优方案
解决方案:跨生命周期的异步任务同步
针对你的场景,DispatchGroup这类内存级同步机制并不适用——因为APP重启后,DispatchGroup实例会被销毁,之前的enter/leave状态完全丢失,且额外调用leave会导致计数负数触发崩溃。解析debugDescription的方式属于hack,依赖苹果未公开的格式,完全不可靠。
下面是两个更稳健的方案,核心思路是通过持久化状态跟踪来实现跨APP生命周期的任务同步:
方案一:状态持久化+全局检查(最通用)
因为视频上传可能跨APP重启,必须把两个任务的执行状态持久化存储,每次任务状态变更时检查是否满足“全部成功”的条件。
实现步骤
- 定义任务状态枚举,用
UserDefaults/CoreData存储状态(示例用UserDefaults) - 每个任务完成(包括重启后的上传回调)时更新状态,并触发全局检查
- APP启动时自动检查未完成任务,失败则重传,再执行状态检查
import UIKit enum TaskStatus: String { case pending, success, failed } // 状态存储工具类 struct TaskStateManager { private static let prefix = "TaskStatus_" static func updateStatus(for taskId: String, to status: TaskStatus) { UserDefaults.standard.set(status.rawValue, forKey: "\(prefix)\(taskId)") } static func status(for taskId: String) -> TaskStatus { let rawValue = UserDefaults.standard.string(forKey: "\(prefix)\(taskId)") ?? "pending" return TaskStatus(rawValue: rawValue) ?? .pending } static func resetAll() { UserDefaults.standard.removeObject(forKey: "\(prefix)normalAPI") UserDefaults.standard.removeObject(forKey: "\(prefix)videoUpload") } } // 任务完成后的处理逻辑 func checkAndPerformTargetAction() { let normalTaskSuccess = TaskStateManager.status(for: "normalAPI") == .success let uploadTaskSuccess = TaskStateManager.status(for: "videoUpload") == .success guard normalTaskSuccess && uploadTaskSuccess else { return } // 执行你需要的特定操作 performTargetTask() // 重置状态,避免重复触发 TaskStateManager.resetAll() } // 普通API任务完成回调 func normalAPIFinished(success: Bool) { let status = success ? TaskStatus.success : TaskStatus.failed TaskStateManager.updateStatus(for: "normalAPI", to: status) checkAndPerformTargetAction() } // 视频上传任务完成回调(包括后台/重启后的回调) func videoUploadFinished(success: Bool) { let status = success ? TaskStatus.success : TaskStatus.failed TaskStateManager.updateStatus(for: "videoUpload", to: status) checkAndPerformTargetAction() } // APP启动时的初始化检查 func applicationDidFinishLaunching(_ application: UIApplication) { // 检查视频上传是否失败,触发重传 if TaskStateManager.status(for: "videoUpload") == .failed { retryVideoUpload() } // 检查是否已经满足全部成功的条件 checkAndPerformTargetAction() } // 后台上传会话的回调处理(如果用URLSession后台上传) func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) { guard let session = URLSession.backgroundSession(withIdentifier: identifier) else { completionHandler() return } session.getAllTasks { tasks in for task in tasks where task.state == .completed { let success = (task.response as? HTTPURLResponse)?.statusCode == 200 videoUploadFinished(success: success) } completionHandler() } }
方案二:Combine+状态持久化(Swift项目推荐)
如果你的项目使用Combine框架,可以用Publishers.Zip合并两个任务的结果,但仍需配合状态持久化处理APP重启的场景:
import Combine var cancellables = Set<AnyCancellable>() // 普通API的Publisher func normalAPIPublisher() -> Future<Bool, Error> { Future { promise in // 发起普通API请求 APIClient.requestNormalAPI { success, error in if let error = error { promise(.failure(error)) } else { promise(.success(success)) } } } } // 视频上传的Publisher func videoUploadPublisher() -> Future<Bool, Error> { Future { promise in VideoUploader.startBackgroundUpload { success, error in if let error = error { promise(.failure(error)) } else { promise(.success(success)) } } } } // 启动任务并合并结果 func startAllTasks() { Publishers.Zip(normalAPIPublisher(), videoUploadPublisher()) .sink(receiveCompletion: { completion in if case .failure(let error) = completion { print("任务失败: \(error.localizedDescription)") // 按需更新失败状态 if error.isVideoUploadError { TaskStateManager.updateStatus(for: "videoUpload", to: .failed) } else { TaskStateManager.updateStatus(for: "normalAPI", to: .failed) } } }, receiveValue: { (normalSuccess, uploadSuccess) in if normalSuccess && uploadSuccess { performTargetTask() TaskStateManager.resetAll() } else { // 更新失败状态 TaskStateManager.updateStatus(for: "normalAPI", to: normalSuccess ? .success : .failed) TaskStateManager.updateStatus(for: "videoUpload", to: uploadSuccess ? .success : .failed) } }) .store(in: &cancellables) }
内容的提问来源于stack exchange,提问作者Wahab Khan Jadon
相关产品推荐
相关产品推荐

