如何实现带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 anduploadGroup.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
notifyblock 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
相关产品推荐
相关产品推荐

