从包含闭包的 Swift 函数返回值

Returning a value from a Swift function containing a closure

我编写了一个函数,它应该 return 一个值,但该值来自闭包。问题是,如果我尝试 return 来自闭包内部的值,它会将其视为来自完成处理程序的 return 值。

private func loadData() throws -> [Item] {
    var items = [Item]()
    let jsonUrl = "http://api.openweathermap.org/data/2.5/forecast/daily?units=metric&cnt=7&q=coventry,uk"
    print(jsonUrl)
    let session = NSURLSession.sharedSession()
    guard let shotsUrl = NSURL(string: jsonUrl) else {
        throw JSONError.InvalidURL(jsonUrl)
    }
    session.dataTaskWithURL(shotsUrl, completionHandler: {(data, response, error) -> Void in
        do {
            let json = try NSJSONSerialization.JSONObjectWithData(data!, options: [])
            print(json)
            guard let days:[AnyObject] = (json["list"] as! [AnyObject]) else {
                throw JSONError.InvalidArray
            }
            for day in days {
                guard let timestamp:Double = day["dt"] as? Double else {
                    throw JSONError.InvalidKey("dt")
                }
                print(timestamp)
                let date = NSDate(timeIntervalSince1970: NSTimeInterval(timestamp))
                guard let weather:[AnyObject] = day["weather"] as? [AnyObject] else {
                    throw JSONError.InvalidArray
                }
                guard let desc:String = weather[0]["description"] as? String else {
                    throw JSONError.InvalidKey("description")
                }
                guard let icon:String = weather[0]["icon"] as? String else {
                    throw JSONError.InvalidKey("icon")
                }
                guard let url = NSURL(string: "http://openweathermap.org/img/w/\(icon).png") else {
                    throw JSONError.InvalidURL("http://openweathermap.org/img/w/\(icon).png")
                }
                guard let data = NSData(contentsOfURL: url) else {
                    throw JSONError.InvalidData
                }
                guard let image = UIImage(data: data) else {
                    throw JSONError.InvalidImage
                }
                guard let temp:AnyObject = day["temp"] else {
                    throw JSONError.InvalidKey("temp")
                }
                guard let max:Float = temp["max"] as? Float else {
                    throw JSONError.InvalidKey("max")
                }
                let newDay = Item(date: date, description: desc, maxTemp: max, icon: image)
                print(newDay)
                items.append(newDay)
            }
            return items // this line fails because I'm in the closure. I want this to be the value returned by the loadData() function.
        } catch {
            print("Fetch failed: \((error as NSError).localizedDescription)")
        }
    })
}

将完成处理程序(在我的示例中名为 dataHandler)添加到您的 loadData 函数:

private func loadData(dataHandler: ([Item])->()) throws {
    var items = [Item]()
    let jsonUrl = "http://api.openweathermap.org/data/2.5/forecast/daily?units=metric&cnt=7&q=coventry,uk"
    print(jsonUrl)
    let session = NSURLSession.sharedSession()
    guard let shotsUrl = NSURL(string: jsonUrl) else {
        throw JSONError.InvalidURL(jsonUrl)
    }
    session.dataTaskWithURL(shotsUrl, completionHandler: {(data, response, error) -> Void in
        do {
            let json = try NSJSONSerialization.JSONObjectWithData(data!, options: [])
            print(json)
            guard let days:[AnyObject] = (json["list"] as! [AnyObject]) else {
                throw JSONError.InvalidArray
            }
            for day in days {
                guard let timestamp:Double = day["dt"] as? Double else {
                    throw JSONError.InvalidKey("dt")
                }
                print(timestamp)
                let date = NSDate(timeIntervalSince1970: NSTimeInterval(timestamp))
                guard let weather:[AnyObject] = day["weather"] as? [AnyObject] else {
                    throw JSONError.InvalidArray
                }
                guard let desc:String = weather[0]["description"] as? String else {
                    throw JSONError.InvalidKey("description")
                }
                guard let icon:String = weather[0]["icon"] as? String else {
                    throw JSONError.InvalidKey("icon")
                }
                guard let url = NSURL(string: "http://openweathermap.org/img/w/\(icon).png") else {
                    throw JSONError.InvalidURL("http://openweathermap.org/img/w/\(icon).png")
                }
                guard let data = NSData(contentsOfURL: url) else {
                    throw JSONError.InvalidData
                }
                guard let image = UIImage(data: data) else {
                    throw JSONError.InvalidImage
                }
                guard let temp:AnyObject = day["temp"] else {
                    throw JSONError.InvalidKey("temp")
                }
                guard let max:Float = temp["max"] as? Float else {
                    throw JSONError.InvalidKey("max")
                }
                let newDay = Item(date: date, description: desc, maxTemp: max, icon: image)
                print(newDay)
                items.append(newDay)
            }
            dataHandler(items)
        } catch {
            print("Fetch failed: \((error as NSError).localizedDescription)")
        }
    }).resume()
}

do {
    try loadData { itemsArray in
        print(itemsArray)
    }
} catch {
    print(error)
}

我已经在 Playground 中对其进行了测试,它可以正常运行: