Сетевой запрос в цикле вылетает приложение - PullRequest
0 голосов
/ 19 мая 2018

У меня есть ошибка, из-за которой я не понимаю, почему.
Моя проблема в том, что у меня есть три функции, имена которых - 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)
            }
        })
    }

вот захват моего экрана при падении приложения: захват XCode при падении приложения Спасибо за продвинутый

1 Ответ

0 голосов
/ 20 мая 2018

Я нашел решение: вот код, который должен работать:

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)
}
...