You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中多API请求(含视频上传)完成后执行操作的最优方案

解决方案:跨生命周期的异步任务同步

针对你的场景,DispatchGroup这类内存级同步机制并不适用——因为APP重启后,DispatchGroup实例会被销毁,之前的enter/leave状态完全丢失,且额外调用leave会导致计数负数触发崩溃。解析debugDescription的方式属于hack,依赖苹果未公开的格式,完全不可靠。

下面是两个更稳健的方案,核心思路是通过持久化状态跟踪来实现跨APP生命周期的任务同步:


方案一:状态持久化+全局检查(最通用)

因为视频上传可能跨APP重启,必须把两个任务的执行状态持久化存储,每次任务状态变更时检查是否满足“全部成功”的条件。

实现步骤

  1. 定义任务状态枚举,用UserDefaults/CoreData存储状态(示例用UserDefaults)
  2. 每个任务完成(包括重启后的上传回调)时更新状态,并触发全局检查
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 23:54:53