forEach循环跳过最后一项

时间:2017-12-03 19:48:12

标签: ios swift firebase-realtime-database eureka-forms

这个forEach循环有时会起作用,有时它会跳过。我不确定我在这里做错了什么。循环将跳过最后一项,永远不会退出。所以完成块根本不会被触发。

我正在使用firebase,Eureka表格和它的ImageRow扩展程序。

我会在此感谢一些帮助。

//MARK: - Get Form Values
var returnedValues: [String: Any] = [:]
fileprivate func getFormValues(values: [String: Any], completion: @escaping ([String:Any])->()) {

    if let name = values["name"] as? String,
        let description = values["description"] as? String,
        let images = values["images"] as? [UIImage],
        let category = values["category"] as? String,
        let price = values["price"] as? Double,
        let deliveryFee = values["deliveryFee"] as? Double,
        let deliveryAreas = values["deliveryArea"] as? Set<String>,
        let deliveryTime = values["deliveryTime"] as? String {

        guard let uid = Auth.auth().currentUser?.uid else { return }
        var imagesData = [[String: Any]]()
        var counter = 0

        images.forEach({ (image) in

            let imageName = NSUUID().uuidString
            let productImageStorageRef = Storage.storage().reference().child("product_images").child(uid).child("\(imageName).jpg")
            var resizedImage = UIImage()

            if image.size.width > 800 {
                resizedImage = image.resizeWithWidth(width: 800)!
            }

            if let uploadData = UIImageJPEGRepresentation(resizedImage, 0.5) {
                productImageStorageRef.putData(uploadData, metadata: nil, completion: { (metadata, error) in
                    if error != nil {
                        print("Failed to upload image: \(error?.localizedDescription ?? "")")
                        return
                    }

                    //Successfully uploaded product Image
                    print("Successfully uploaded product Image")
                    if let productImageUrl = metadata?.downloadURL()?.absoluteString {
                        counter += 1
                        let imageData: [String: Any] = [imageName: productImageUrl]
                        imagesData.append(imageData)

                        if counter == images.count {
                            let deliveryAreasArr = Array(deliveryAreas)
                            self.returnedValues = ["name": name, "description": description, "images": imagesData , "category": category, "price": price, "deliveryFee": deliveryFee, "deliveryArea": deliveryAreasArr, "deliveryTime": deliveryTime, "creationDate": Date().timeIntervalSince1970, "userId": uid]
                            completion(self.returnedValues)
                        }

                    }

                })
            }
        })

    } else {

        let alert = UIAlertController(title: "Missing Information", message: "All fields are required. Please fill all fields.", preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (_) in
            alert.dismiss(animated: true, completion: nil)
        }))
        UIActivityIndicatorView.stopActivityIndicator(indicator: self.activityIndicator, container: self.activityIndicatorContainer, loadingView: self.activityIndicatorLoadingView)
        self.present(alert, animated: true, completion: nil)
    }
}

1 个答案:

答案 0 :(得分:0)

if循环中有许多for语句可能导致counter没有递增。如果其中任何一个失败,那么你永远不会调用完成处理程序。

我知道您正在使用counter试图了解所有异步任务何时完成,但调度组是更好的解决方案。

在所有路径中调用完成处理程序也很重要;例如,当初始guard失败或在初始else的{​​{1}}子句中时 - 您的完成处理程序应该接受if参数,以便它知道存在问题

Error

其他一些观点:

  • 传递结构而不是字典会更好。使用//MARK: - Get Form Values fileprivate func getFormValues(values: [String: Any], completion: @escaping ([String:Any]?)->()) { var returnedValues: [String: Any] = [:] if let name = values["name"] as? String, let description = values["description"] as? String, let images = values["images"] as? [UIImage], let category = values["category"] as? String, let price = values["price"] as? Double, let deliveryFee = values["deliveryFee"] as? Double, let deliveryAreas = values["deliveryArea"] as? Set<String>, let deliveryTime = values["deliveryTime"] as? String { guard let uid = Auth.auth().currentUser?.uid else { completion(nil) return } var imagesData = [[String: Any]]() let dispatchGroup = DispatchGroup() // Create a Dispatch Group images.forEach({ (image) in let imageName = NSUUID().uuidString let productImageStorageRef = Storage.storage().reference().child("product_images").child(uid).child("\(imageName).jpg") var resizedImage = UIImage() if image.size.width > 800 { resizedImage = image.resizeWithWidth(width: 800)! } if let uploadData = UIImageJPEGRepresentation(resizedImage, 0.5) { dispatchGroup.enter() // Enter the group productImageStorageRef.putData(uploadData, metadata: nil, completion: { (metadata, error) in guard error == nil else { print("Failed to upload image: \(error?.localizedDescription ?? "")") dispatchGroup.leave() // Leave the dispatch group if there was an error return } //Successfully uploaded product Image print("Successfully uploaded product Image") if let productImageUrl = metadata?.downloadURL()?.absoluteString { let imageData: [String: Any] = [imageName: productImageUrl] imagesData.append(imageData) } dispatchGroup.leave() // Leave the dispatch group in normal circumstances }) } }) // Schedule a notify closure for execution when the dispatch group is empty dispatchGroup.notify(queue: .main) { let deliveryAreasArr = Array(deliveryAreas) returnedValues = ["name": name, "description": description, "images": imagesData , "category": category, "price": price, "deliveryFee": deliveryFee, "deliveryArea": deliveryAreasArr, "deliveryTime": deliveryTime, "creationDate": Date().timeIntervalSince1970, "userId": uid] completion(self.returnedValues) } } else { completion(nil) let alert = UIAlertController(title: "Missing Information", message: "All fields are required. Please fill all fields.", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (_) in alert.dismiss(animated: true, completion: nil) })) UIActivityIndicatorView.stopActivityIndicator(indicator: self.activityIndicator, container: self.activityIndicatorContainer, loadingView: self.activityIndicatorLoadingView) self.present(alert, animated: true, completion: nil) } } 作为输入将在函数开头摆脱那个巨大的struct,因为你会知道值的类型,并使它们成为你知道的结构的非可选属性价值观存在。
  • 这样的功能发出警报是不常见的;它通常只是通过完成返回一个错误,或者可能if let返回给调用者以指示存在问题并让调用者处理它
  • 我不明白为什么throw需要成为一系列词典。数组中的每个字典只有一个条目,因此您可以使用imagesData的字典(当您知道类型将是什么时,无需使用[String:String]