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

如何实现带For循环的异步图片上传Completion Handler,确保URL存入字典

Fixing Image Upload Completion Handling for Multiple Images

Got it, let's sort out your completion handler so it only triggers once every image finishes uploading, and all their download URLs are properly stored in your dictionary. The core problem with your current code is that you're kicking off asynchronous upload tasks in a loop, but the completion will fire right away before any uploads finish. We'll use a DispatchGroup to track all upload operations and wait for them to complete.

Here's the revised code with proper completion handling:

func saveImages(images: [UIImage], completion: @escaping ([String : Any], String?) -> Void) { 
    let ref = FirDatabaseReference.courts.reference() 
    let upload = ref.childByAutoId() 
    let uploadID = upload.key 
    var urlDict = [String : Any]()
    var errorMessage: String?
    
    // Create a dispatch group to track all upload tasks
    let uploadGroup = DispatchGroup()
    
    for i in 0..<images.count { 
        uploadGroup.enter() // Mark that a task is starting
        
        let saveID = "\(uploadID)_\(i)" 
        // Assuming you're using Firebase Storage for image uploads
        let storageRef = Storage.storage().reference().child("court_images/\(saveID)")
        
        // Convert UIImage to JPEG data (adjust compression quality as needed)
        guard let imageData = images[i].jpegData(compressionQuality: 0.8) else {
            errorMessage = "Failed to convert image \(i) to data"
            uploadGroup.leave() // Don't forget to leave even if conversion fails
            continue
        }
        
        // Start the asynchronous upload task
        let uploadTask = storageRef.putData(imageData, metadata: nil) { metadata, error in
            defer { uploadGroup.leave() } // Ensure we leave the group no matter what happens
            
            if let error = error {
                errorMessage = "Failed to upload image \(i): \(error.localizedDescription)"
                return
            }
            
            // Fetch the download URL once upload succeeds
            storageRef.downloadURL { url, error in
                if let downloadURL = url {
                    urlDict[saveID] = downloadURL.absoluteString
                } else if let error = error {
                    errorMessage = "Failed to get download URL for image \(i): \(error.localizedDescription)"
                }
            }
        }
    }
    
    // Trigger completion only when all upload tasks are done
    uploadGroup.notify(queue: .main) {
        completion(urlDict, errorMessage)
    }
}

Key Changes Explained:

  • DispatchGroup: We use uploadGroup.enter() before each upload starts and uploadGroup.leave() once it finishes (success or failure). This guarantees we wait for every task to complete before firing the completion.
  • Error Tracking: We capture errors during image conversion, upload, or URL retrieval, and pass them to the completion handler for you to handle.
  • Defer Statement: The defer { uploadGroup.leave() } ensures we always exit the group even if an error occurs, preventing deadlocks.
  • Main Queue Notification: The notify block runs on the main queue, so you can safely update UI from the completion handler if needed.

How to Use This:

When you call saveImages, you'll receive the full dictionary of URLs only after all uploads finish, along with any error message if something went wrong:

saveImages(images: yourImageArray) { urlDict, error in
    if let error = error {
        print("Upload failed: \(error)")
        // Handle error (show alert, retry, etc.)
    } else {
        print("All images uploaded! URLs: \(urlDict)")
        // Proceed with saving URLs to your database or other logic
    }
}

内容的提问来源于stack exchange,提问作者AlexVilla147

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 12:28:16