Быстрое обновление глобальной переменной вне закрытия с помощью обработчика завершения

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

import Foundation
import PlaygroundSupport

func pdate(month: String, day: String, year: String) -> String {
    let pdate = month + "/" + day + "/" + year
    return pdate
}

struct Phenomenon {
    var phenom: String
    var date: String
    var time: String
}

struct Apsides: Codable {
    let data: [Apsdata]?
    struct Apsdata: Codable {
        let phenom: String
        let year: String
        let month: String
        let day: String
        let time: String
    }
}

var arrFromURL: [(Phenomenon)] = []  //define a global I can use later- I know, globals are evil

func doURLsessions(completionHandler: @escaping (Phenomenon?)->()) {
    let sessionApsides = URLSession(configuration: URLSessionConfiguration.default)
    if let timeurl = URL(string: "http://api.usno.navy.mil/seasons?year=2018&tz=-8&dst=true") {
        (sessionApsides.dataTask(with: timeurl, completionHandler: { (data, response, error) in
            if let error = error {
                print("Error: \(error)")
            } else if let data = data {
                let jsonDecoder = JSONDecoder()
                let response = try? jsonDecoder.decode(Apsides.self, from: data)
                if (response?.data != nil) {
                    for item in [response?.data] {
                        for p in item! {
                            switch p.phenom {
                            case "Perihelion":
                                let perihelion = Phenomenon.init(
                                    phenom: p.phenom, date: pdate(month: p.month, day: p.day, year: p.year), time: p.time)
                                completionHandler(perihelion)
                            case "Aphelion":
                                let aphelion = Phenomenon.init(
                                    phenom: p.phenom, date: pdate(month: p.month, day: p.day, year: p.year), time: p.time)
                                completionHandler(aphelion)
                            default: ()
                            }
                        }
                    }
                }
            }
            //print(arrFromURL) //This works fine, but not what I want
        })).resume()
    }
}

func chPhenom(phenom: String, date: String, time: String) {
    let p = Phenomenon.init(phenom: phenom, date: date, time: time)
    arrFromURL.append(p)
    //print(arrFromURL) //Also works fine,  but I want it's contents avilable outside this function
}

doURLsessions(completionHandler: {Phenomenon in
    chPhenom(phenom: Phenomenon!.phenom, date: Phenomenon!.date, time: Phenomenon!.time)
})

print(arrFromURL) //This just prints []- why isn't the global var available here? Is it printed before the asynch func finishes?

PlaygroundPage.current.needsIndefiniteExecution = true

person Chris Taylor    schedule 01.03.2018    source источник
comment
Этот URL-адрес не работает. Даже из браузера. Выдает ошибку тайм-аута.   -  person Mehul Parmar    schedule 01.03.2018
comment
Я только что попробовал, api.usno.navy.mil/seasons? год = 2018 & tz = -8 & dst = true. Кажется, у меня работает?   -  person Chris Taylor    schedule 01.03.2018
comment
@ChrisTaylor Вы правильно поняли, print(arrFromURL) вызывается до вызова completionHandler. Итак, вы просто печатаете пустой массив.   -  person aunnnn    schedule 01.03.2018


Ответы (1)


Код в функции doURLsessions является асинхронным - он не блокирует выполнение другого кода, и, поскольку это сетевой запрос, для его завершения требуется некоторое время. В вашем случае print(arrFromURL) вызывается до завершения doURLsessions, поэтому arrFromURL все еще пуст.

person Karlis    schedule 01.03.2018
comment
Спасибо. Как сделать то, что производит doURLsessions, доступным вне его области действия или области действия обработчика завершения? - person Chris Taylor; 01.03.2018
comment
Вам нужно либо подождать и получить доступ к результату после завершения doURLsessions, либо постоянно отслеживать arrFromURL и что-то делать при изменении его значения. - person Karlis; 02.03.2018
comment
Но ваш arrFromURL доступен внутри doURLsessions обработчика завершения, я бы просто продолжил вашу работу оттуда: doURLsessions(completionHandler: {Phenomenon in chPhenom(phenom: Phenomenon!.phenom, date: Phenomenon!.date, time: Phenomenon!.time) refresh() }) func refresh() { print(arrFromURL) } - person Karlis; 02.03.2018
comment
Хорошие моменты: мне нужен доступ к содержимому массива, чтобы обновить метку в моем контроллере представления, что у меня установлено соединение IBAction, и я не могу сделать это изнутри своего закрытия обработчика завершения. Как вы предлагаете, я постараюсь следить за обновлениями в массиве. - person Chris Taylor; 02.03.2018