如何在我的其余代码执行之前从 URLSession 获得响应?

How do I get the response from a URLSession before the rest of my code executes?

我在 Swift 中的 URLSession 有问题。无论我尝试什么(或者我阅读了多少教程),我都找不到一种方法让网络请求在 执行我的其余代码之前完成。我的印象是完成处理程序可以解决问题,但事实并非如此。我在这里做错了什么?

struct DataManager {
    
    let eventsURL = URL(string: "https://spreadsheets.google.com/feeds/list/blablabla/1/public/full?alt=json")!
    let decoder = JSONDecoder()
    var data = Data()
    
    func downloadEvents() -> [Event] {
        var events: [Event] = []
        var downloadedEvents: [[String: Any]] = []
        
        getDataFromServer(forURL: eventsURL) { result in
            downloadedEvents = result
        }
        
        // This next part always executes before the response is receive, which means the downloadedEvents variable is always empty.

        for downloadedEvent in downloadedEvents {
            if let event = Event(fromJSON: downloadedEvent) {
                events.append(event)
            }
        }
        
        return events
    }
    
    func getDataFromServer(forURL url: URL, completion: @escaping (_ result: [[String: Any]]) -> Void) {
        URLSession.shared.dataTask(with: url) { data, response, error in
            if let jsonData = data {
                let jsonObject = try? JSONSerialization.jsonObject(with: jsonData, options: [])
                if let dictionary = jsonObject as? [String: Any] {
                    if let feed = dictionary["feed"] as? [String: Any] {
                        if let entry = feed["entry"] as? [[String: Any]] {
                            DispatchQueue.main.async {
                                completion(entry)
                            }
                        }
                    }
                }
            }
        }.resume()
    }
}

您的 downloadEvents 函数需要异步,因为它调用 另一个 异步函数 getDataFromServer.

解决方案可能是将计算Events的部分移到异步块中,并提供一个完成参数:

func downloadEvents(completion: @escaping ([Event]) -> ()) {
    var events: [Event] = []
    var downloadedEvents: [[String: Any]] = []

    getDataFromServer(forURL: eventsURL) { result in
        downloadedEvents = result
        for downloadedEvent in downloadedEvents {
            if let event = Event(fromJSON: downloadedEvent) {
                events.append(event)
            }
        }
        completion(events)
    }
}

备注

您的代码实际上可以简化为:

func downloadEvents(completion: @escaping ([Event]) -> ()) {
    getDataFromServer(forURL: eventsURL) { completion([=11=].compactMap(Event.init(fromJSON:))) }
}