循环中的网络请求使应用程序崩溃

时间:2018-05-19 20:34:18

标签: ios swift loops network-programming grand-central-dispatch

我有一个我不明白为什么的错误 我的问题是我有三个函数,其名称是sendCourse,sendRequire,sendStep 第一个函数sendCourse很简单。此功能只是一个基本的网络查询 sendRequire和sendStep函数类似。它们包含一个for循环,它迭代一个数组以将数组的每个元素发送到服务器。在此函数中,必须等到上一个网络请求完成(该元素被发送到服务器)才能发送数组的下一个元素。
最后,我有另一个名为sendRecipe的方法。此方法执行sendCourse,sendRequire,sendStep。 senRecipe必须等待sendCourse完成。当sendCourse完成后,sendRecipe函数必须执行sendRequire。然后sendRecipe必须等到sendRequire完成才能执行sendStep 最后,我有一个调用sendRecipe的按钮 当我点击按钮时,我的应用程序崩溃了 这是我的代码:

    private func sendCourse(success: @escaping(() -> Void), errorHandling: @escaping((String) -> Void)) {
        let dataCourse:[String:String] = [
            "name":self.course.name,
            "course_type_id":String(self.course.type.id),
            "country_code":self.course.country.countryCode,
            "dishes_number":String(self.course.dishesNumber),
            "description":self.course.description
        ]
        APIManager.sharedInstance.put(action: .courses, data: dataCourse, onSuccess: {(responseCode, data) -> Void in
            if responseCode == 201 {
                let jsonDecoder = JSONDecoder()
                if let course = try? jsonDecoder.decode(Courses.self, from: data) {
                    self.course = course
                    success()
                } else {
                    errorHandling("service unavailable")
                }
            } else {
                errorHandling("service unavailable")
            }
        }, onFailure: {(error) -> Void in
            errorHandling(error.localizedDescription)
        })
    }

    private func sendRequire(success: @escaping(() -> Void), errorHandling: @escaping((String) -> Void)) {
        let requireGroup = DispatchGroup()
        var fail = false
        for require in requires {
            if fail == true {
                break
            }
            requireGroup.enter()
            let dataRequire:[String:String] = [
                "course_id":String(self.course.id!),
                "ingredient_id":String(require.ingredient.id),
                "quantity":String(format: "%f", require.quantity)
            ]
            APIManager.sharedInstance.put(action: .requires, data: dataRequire, onSuccess: {(responseCode, data) -> Void in
                if responseCode == 201 {
                    requireGroup.leave()
                    success()
                } else {
                    requireGroup.leave()
                    fail = true
                    errorHandling("Service unavailable")
                }
            }, onFailure: {(error) -> Void in
                requireGroup.leave()
                fail = true
                errorHandling(error.localizedDescription)})
        }
        requireGroup.wait()
    }
    private func sendStep(success: @escaping(() -> Void), errorHandling: @escaping((String) -> Void)) {
        let stepGroup = DispatchGroup()
        var fail = false
        for (index, step) in self.steps.enumerated() {
            if fail == true {
                break
            }
            stepGroup.enter()
            var previousStepId=0
            if index == 0 {
                previousStepId = 0
            } else {
                previousStepId = steps[index-1].id!
            }
            let dataStep:[String:String] = [
                "course_id":String(self.course.id!),
                "description":step.description,
                "duration_hours":String(step.durationHours),
                "duration_minutes":String(step.durationMinutes),
                "duration_seconds":String(step.durationSeconds),
                "previous_step_id":String(previousStepId)

            ]
            APIManager.sharedInstance.put(action: .steps, data: dataStep, onSuccess: {(responseCode, data) -> Void in
                if responseCode == 201 {
                    let jsonDecoder = JSONDecoder()
                    if let step = try? jsonDecoder.decode(Step.self, from: data) {
                        self.steps[index] = step
                        stepGroup.leave()
                        success()
                    } else {
                        fail = true
                        errorHandling("service unavailable")
                        stepGroup.leave()
                    }
                } else {
                    fail = true
                    errorHandling("service unavailable")
                    stepGroup.leave()
                }
            }, onFailure: {(error) -> Void in
                errorHandling(error.localizedDescription)
                fail = true
                stepGroup.leave()
            })
            stepGroup.wait()
        }
    }
    func sendRecipe(errorHandling: @escaping((String) -> Void)) {
        DispatchQueue.global(qos: .background).sync {
            self.sendCourse(success: {}, errorHandling: errorHandling)
        }
        DispatchQueue.global(qos: .background).sync {
            self.sendRequire(success: {}, errorHandling: errorHandling)
        }
        DispatchQueue.global(qos: .background).sync {
            self.sendStep(success: {}, errorHandling: errorHandling)
        }
    }
here is the code of the button:
    @IBAction func doneBarButtonItemTapped(_ sender: UIBarButtonItem) {
        recipe?.steps = steps
        recipe?.sendRecipe(errorHandling: {(error) -> Void in
            DispatchQueue.main.async {
                let alert = UIAlertController(title: "Error", message: error, preferredStyle: .alert)
                alert.addAction(UIAlertAction(title: "dismiss", style: .default, handler: nil))
                self.present(alert, animated: true, completion: nil)
            }
        })
    }

这是应用程序崩溃时我的屏幕捕获: capture of Xcode when the app crash 先谢谢你了

1 个答案:

答案 0 :(得分:0)

我找到了解决方案: 这是应该工作的代码:

private func sendCourse(success: @escaping(() -> Void), errorHandling: @escaping((String) -> Void)) {
    let dataCourse:[String:String] = [
        "name":self.course.name,
        "course_type_id":String(self.course.type.id),
        "country_code":self.course.country.countryCode,
        "dishes_number":String(self.course.dishesNumber),
        "description":self.course.description
    ]
    DispatchQueue.global(qos: .utility).sync {
    APIManager.sharedInstance.put(action: .courses, data: dataCourse, onSuccess: {(responseCode, data) -> Void in
        print(responseCode, String(data:data, encoding: String.Encoding.utf8)!)
        if responseCode == 201 {
            let jsonDecoder = JSONDecoder()
            if let course = try? jsonDecoder.decode(Courses.self, from: data) {
                self.course = course
                success()
            } else {
                errorHandling("service unavailable")
            }
        } else {
            errorHandling("service unavailable")
        }
    }, onFailure: {(error) -> Void in
        errorHandling(error.localizedDescription)
    })
    }
}

private func sendRequire(success: @escaping(() -> Void), errorHandling: @escaping((String) -> Void)) {
    let requireGroup = DispatchGroup()
    var fail = false
    for (index, require) in requires.enumerated() {
        if fail == true {
            break
        }
        requireGroup.enter()
        let dataRequire:[String:String] = [
            "course_id":String(self.course.id ?? 0),
            "ingredient_id":String(require.ingredient.id),
            "quantity":String(format: "%f", require.quantity)
        ]
        APIManager.sharedInstance.put(action: .requires, data: dataRequire, onSuccess: {(responseCode, data) -> Void in
            print(responseCode, String(data:data, encoding: String.Encoding.utf8)!)
            if responseCode == 201 {
                requireGroup.leave()
            } else {
                fail = true
                errorHandling("Service unavailable")
            }
        }, onFailure: {(error) -> Void in
            fail = true
            errorHandling(error.localizedDescription)
        })
        requireGroup.wait()
        if index==requires.count-1 {
            success()
        }
    }
}
private func sendStep(success: @escaping(() -> Void), errorHandling: @escaping((String) -> Void)) {
    let stepGroup = DispatchGroup()
    var fail = false
    for (index, step) in self.steps.enumerated() {
        if fail == true {
            break
        }
        stepGroup.enter()
        var previousStepId=0
        if index == 0 {
            previousStepId = 0
        } else {
            previousStepId = steps[index-1].id!
        }
        let dataStep:[String:String] = [
            "course_id":String(self.course.id ?? 0),
            "description":step.description,
            "duration_hours":String(step.durationHours),
            "duration_minutes":String(step.durationMinutes),
            "duration_seconds":String(step.durationSeconds),
            "previous_step_id":String(previousStepId)

        ]
        APIManager.sharedInstance.put(action: .steps, data: dataStep, onSuccess: {(responseCode, data) -> Void in
            print(responseCode, String(data:data, encoding: String.Encoding.utf8)!)
            if responseCode == 201 {
                let jsonDecoder = JSONDecoder()
                if let step = try? jsonDecoder.decode(Step.self, from: data) {
                    self .steps[index] = step
                    stepGroup.leave()
                } else {
                    fail = true
                    errorHandling("service unavailable")
                }
            } else {
                fail = true
                errorHandling("service unavailable")
            }
        }, onFailure: {(error) -> Void in
            errorHandling(error.localizedDescription)
            fail = true
        })
        stepGroup.wait()
        if index==steps.count-1 {
            success()
        }
    }
}
func sendRecipe(errorHandling: @escaping((String) -> Void)) {
    let recipeGroup = DispatchGroup()
    recipeGroup.enter()
    self.sendCourse(success: {recipeGroup.leave()}, errorHandling: errorHandling)
    recipeGroup.wait()
    recipeGroup.enter()
    self.sendRequire(success: {recipeGroup.leave()}, errorHandling: errorHandling)
    recipeGroup.wait()
    recipeGroup.enter()
    self.sendStep(success: {recipeGroup.leave()}, errorHandling: errorHandling)
}