Куда идут команды Dispatch Group в коде? - PullRequest
0 голосов
/ 01 мая 2019

Я пытаюсь запустить функцию X раз в цикле for in, но когда все функции вернулись, я хочу запустить другую функцию.

В настоящее время он работает, задерживая последнюю функцию на 1 секунду, но мне бы очень хотелось, чтобы работала Dispatch Group.

Я просматривал различные онлайн-примеры и другие вопросы, но, похоже, ничего из того, что я пробовал, не работает. Код, который я знаю на данный момент, не будет работать, так как он запускает dispatchGroup.leave () каждый раз, когда функции in in отправлены, а не когда они возвращаются.

Я пытался поместить код DispatchGroup в функцию (которая находится в другом файле), но я в замешательстве, хотя я думаю, что близок к решению.

Я также смотрел на семафоры и использовал счетчик и увеличивал значение при каждом запуске цикла, но я продолжаю возвращаться к DispatchGroups.

Мое последнее средство - задать вопрос!

Код ViewController

 @IBAction func removeDeviceBtn(_ sender: Any) {

    let dispatchGroup = DispatchGroup()

    for owner in arrOwnerList {
        dispatchGroup.enter()
        self.removeDevice(device: self.device, account: owner as! String, completion: self.completed)
        dispatchGroup.leave()
    }

    dispatchGroup.notify(queue: DispatchQueue.main, execute: {
        self.removeDeviceFromServer(device: self.device)
        self.sendEmail(to:"gordon@example.co.uk", subject:self.device+" has been removed", text:self.device+" has been removed from the server, please check the sim for bar and termination")
    })

Код функции в другом файле как расширение

  func completed(isSuccess: Bool) {
}

func removeDevice(device: String, account: String, completion: @escaping (Bool) -> Void) {

    let dictHeader : [String:String] = ["username":Username,"password":Password]
    let dictArray = [device]

    WebHelper.requestPUTAPIRemoveDevice(BaseURL+"rootaccount/removedevices/"+account+"?server=MUIR", header: dictHeader, dictArray: dictArray, controllerView: self, success: { (response) in
        if response.count == 0 {
            DispatchQueue.main.async {
                GlobalConstant.showAlertMessage(withOkButtonAndTitle: GlobalConstant.AppName, andMessage: Messages.ServerError, on: self)
            }
        }
        else {
            if response.count != 0 {
                let isSuccess = true
                completion(isSuccess)
            }
            else{
                DispatchQueue.main.async {
                    GlobalConstant.showAlertMessage(withOkButtonAndTitle: GlobalConstant.AppName, andMessage: Messages.NoDataFound, on: self)

                }
            }
        }
    }) { (error) in
        DispatchQueue.main.async {
            GlobalConstant.showAlertMessage(withOkButtonAndTitle: GlobalConstant.AppName, andMessage: error?.localizedDescription ?? Messages.ServerError, on: self)
        }
    }
}

Код из файла WebHelper

    class func requestPUTAPIRemoveDevice(_ strURL: String,header: Dictionary<String,String>,dictArray: Array<Any>, controllerView viewController: UIViewController, success: @escaping (_ response: [AnyHashable: Any]) -> Void, failure: @escaping (_ error: Error?) -> Void) {

    if GlobalConstant.isReachable() {
        DispatchQueue.main.async {
            LoadingIndicatorView.sharedInstance.showHUD()
        }
        let loginString = String(format: "%@:%@", header["username"]!, header["password"]!)
        let loginData: Data = loginString.data(using: String.Encoding.utf8)!
        let base64LoginString = loginData.base64EncodedString(options: NSData.Base64EncodingOptions())

        let headers = ["Authorization": "Basic "+base64LoginString, "Referer": "http://www.example.com"]

        let postData = try? JSONSerialization.data(withJSONObject: dictArray, options: [])

        let request = NSMutableURLRequest(url: NSURL(string: strURL)! as URL,
                                          cachePolicy: .useProtocolCachePolicy,
                                          timeoutInterval: 10.0)
        request.httpMethod = "PUT"
        request.allHTTPHeaderFields = headers
        request.httpBody = postData

        let session = URLSession.shared
        let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
            if (error != nil) {
                DispatchQueue.main.async {
                    LoadingIndicatorView.sharedInstance.hideHUD()
                }
                failure(error)
            } else {
                if let httpResponse = response as? HTTPURLResponse {
                    print("Server code \(httpResponse.statusCode)")
                    if httpResponse.statusCode == 200 || httpResponse.statusCode == 208 {
                        DispatchQueue.main.async {
                            LoadingIndicatorView.sharedInstance.hideHUD()
                        }
                        let jsonResult = try? JSONSerialization.jsonObject(with: data!, options:    JSONSerialization.ReadingOptions.mutableContainers)

                        if (jsonResult is NSDictionary) {
                            success(jsonResult as! [AnyHashable : Any])
                        }
                        else if (jsonResult is NSArray) {
                            success(["response":jsonResult as! NSArray])
                        }
                        else{
                          success(["response":httpResponse.statusCode])
                          DispatchQueue.main.async {
                        }

                        }
                    }
                    else{
                        DispatchQueue.main.async {
                            LoadingIndicatorView.sharedInstance.hideHUD()
                        }
                        failure(error)
                    }
                }
            }
        })

        dataTask.resume()
    }
    else {
        DispatchQueue.main.async {
            LoadingIndicatorView.sharedInstance.hideHUD()
            GlobalConstant.showAlertMessage(withOkButtonAndTitle: "", andMessage: "Internet not connected", on: viewController)
        }

    }
}

Окончательное решение (кроме исправления различных других проблем) состояло в добавлении success(["response":httpResponse.statusCode]) в файл WebHelper, исправленный код выше

1 Ответ

2 голосов
/ 01 мая 2019

Поместите leave в обработчик завершения:

for owner in arrOwnerList {
    dispatchGroup.enter()
    removeDevice(device: device, account: owner as! String) { [weak self] success in
        self?.completed(isSuccess: success)
        dispatchGroup.leave()
    }
}

Или, учитывая, что вы на самом деле ничего не делаете в функции completed, я бы просто удалил это:

for owner in arrOwnerList {
    dispatchGroup.enter()
    removeDevice(device: device, account: owner as! String) { _ in
        dispatchGroup.leave()
    }
}

Я заметил, что в removeDevice есть пути выполнения, которые не вызывают обработчик завершения. Убедитесь, что каждый путь выполнения вызывает обработчик завершения, иначе ваша группа рассылки никогда не будет разрешена.

func removeDevice(device: String, account: String, completion: @escaping (Bool) -> Void) {

    let dictHeader = ["username": Username, "password": Password]
    let dictArray = [device]

    WebHelper.requestPUTAPIRemoveDevice(BaseURL+"rootaccount/removedevices/"+account+"?server=MUIR", header: dictHeader, dictArray: dictArray, controllerView: self, success: { response in
        DispatchQueue.main.async {
            if response.count == 0 {
                GlobalConstant.showAlertMessage(withOkButtonAndTitle: GlobalConstant.AppName, andMessage: Messages.ServerError, on: self)
                completion(false)
            } else {
                completion(true)
            }
        }
    }, failure: { error in
        DispatchQueue.main.async {
            GlobalConstant.showAlertMessage(withOkButtonAndTitle: GlobalConstant.AppName, andMessage: error?.localizedDescription ?? Messages.ServerError, on: self)
            completion(false)
        }
    })
}

Между прочим, я не знаю название закрытия «сбой», поэтому я предположил, что это было failure, но отрегулируйте в соответствии с вашим requestPUTAPIRemoveDevice методом. Обычно мы избегаем шаблона множественного замыкания в Swift, но если вы собираетесь это сделать, я бы избежал синтаксиса конечного замыкания. Это делает функциональное назначение этого второго замыкания немного более явным.

Или все это ставит вопрос о том, почему requestPUTAPIRemoveDevice вообще инициирует обновления пользовательского интерфейса. Я, вероятно, поместил бы это в методе контроллера представления. Так что requestPUTAPIRemoveDevice должен просто передавать достаточно информации, чтобы подпрограммы removeDeviceBtn знали, какую ошибку представить. И эта идея представления отдельного сообщения об ошибке для каждой ошибки, вероятно, тоже подозрительна. (Например, если вы потеряли подключение к Интернету и пытаетесь удалить дюжину устройств, вы действительно хотите показать дюжину отдельных сообщений об ошибках?) Но это выходит за рамки этого вопроса.

...