Swift: асинхронная задача DataTask никогда не завершается - PullRequest
0 голосов
/ 03 сентября 2018

У меня есть функция, которая очищает файл JSON, встроенный в веб-сайт, и возвращает массив строк. Поскольку данные, которые он возвращает, очень важны для моего контроллера представления, мне нужно запустить эту функцию до загрузки представления. Он вызывается при инициализации контроллера представления.

func scrapeBuses() -> [String] {
    let config = URLSessionConfiguration.default
    //config.waitsForConnectivity = true
    let defaultSession = URLSession(configuration: config)
    let url = URL(string: link to a JSON file)
    let request = NSMutableURLRequest(url: url!)
    request.cachePolicy = .reloadIgnoringLocalCacheData
    var loops = [String]()
    let group = DispatchGroup()
    group.enter()
    DispatchQueue.main.async {
        let task = defaultSession.dataTask(with: request as URLRequest) { data, response, error in
            do {
                print("Getting information from website")
                if let error = error {
                    print(error.localizedDescription)
                } else if let data = data, let response = response as? HTTPURLResponse, response.statusCode == 200 {
                    print("in async")
                    loops.append("test2")
                }
            }
            catch { print(error)}
        }
    }
    task.resume()
    print("about to return")
    //return loops
    }
    group.wait()
    return loops
}

Поскольку он асинхронный, я добавил операторы DispatchGroup и wait(), чтобы мой основной поток ждал, пока эти важные данные не будут очищены, прежде чем продолжить работу с остальным потоком. Однако я заметил, что когда происходит переход к этому контроллеру вида (то есть, когда контроллер вида инициализируется), ничего больше не печатается, и моделирование останавливается. Ясно, что это означает, что основной поток ожидает завершения scrape(), но почему он работает бесконечно? Когда я захожу на сайт, как в своем веб-браузере, так и в Rested, я вижу, что на нем правильно размещается JSON.

Edit:

Я попробовал другую версию той же функции, используя обработчик завершения вместо DispatchQueue.wait(). Ниже приведен код:

func scrapeBuses(completion: @escaping ([String]) -> Void) {
    let config = URLSessionConfiguration.default
    let defaultSession = URLSession(configuration: config)
    let url = URL(string: "https://www.cmunc.net/assets/appData.json")
    let request = NSMutableURLRequest(url: url!)
    request.cachePolicy = .reloadIgnoringLocalCacheData
    var loops = [String]()

        let task = defaultSession.dataTask(with: request as URLRequest) { data, response, error in
            print("Getting information from website")
            if let error = error {
                print(error.localizedDescription)
            } else if let data = data, let response = response as? HTTPURLResponse, response.statusCode == 200 {
                print("in async")
                loops.append("test2")
            }
            completion(loops)
        }
        task.resume()
}

А вот функция, которая вызывается контроллером вида init:

func refresh() {
    var newArray = [String]()
    scrapeBuses { loops in
        newArray = loops
        print("calling scrapeBuses closure")
    }
    print(newArray)
}

Используя обработчик завершения, код выполняется правильно. Как операторы print в scrapeBuses, так и операторы печати closeure. Огромное спасибо всем, кто предложил свое понимание.

Ответы [ 2 ]

0 голосов
/ 03 сентября 2018

Вы должны добавить обработчик завершения к scrapeBuses функции.

func scrapeBuses(completion: ([String]?, Error?) -> Void) {
    // etc (remove all mentions of groups)

    let task = defaultSession.dataTask(with: request as URLRequest) { data, response, error in
        do {
            print("Getting information from website")
            if let error = error {
                DispatchQueue.main.async { // Call back on the main queue
                    completion(nil, error)
                }
            } else if let data = data, let response = response as? HTTPURLResponse, response.statusCode == 200 {
                print("in async")
                loops.append("test2")
            }
            DispatchQueue.main.async {
                completion(loops, nil)
            }
        } 
        catch { 
            DispatchQueue.main.async {
                completion(nil, error)
            }
        }
    }
    task.resume()
}

тогда ...

func viewDidLoad() {
    super.viewDidLoad() 

    scrapeBuses { loops, error in
        if let error = error {    
            print(error)
            return
        }
        // Do something with loops
    }
}
0 голосов
/ 03 сентября 2018

Потому что вы пропустили

loops.append("test2")
group.leave()

Или лучше в верхней части обратного вызова

defer { group.leave() }

Кстати, лучше иметь индикатор активности, а не wait

//

Попробуйте это

func scrapeBuses(completion:@escaping(_ res:[String]?) -> Void ) {
    let config = URLSessionConfiguration.default
    //config.waitsForConnectivity = true
    let defaultSession = URLSession(configuration: config)
    let url = URL(string: link to a JSON file)
    let request = NSMutableURLRequest(url: url!)
    request.cachePolicy = .reloadIgnoringLocalCacheData
    var loops = [String]()
    let task = defaultSession.dataTask(with: request as URLRequest) { data, response, error in
            do {
                print("Getting information from website")
                if let error = error {
                    print(error.localizedDescription)
                     completion(nil)
                } else if let data = data, let response = response as? HTTPURLResponse, response.statusCode == 200 {
                    print("in async")
                    loops.append("test2")
                    completion(loops)
                }
            }
            catch { print(error)
                     completion(nil)
                  }
        }
    }
    task.resume()

}
...