2015-09-24 3 views
1

Я написал функцию, которая должна вернуть значение, но значение происходит из замыкания. Проблема в том, что если я попытаюсь вернуть значение изнутри закрытия, он рассматривает это как возвращаемое значение из обработчика завершения.Возврат значения из функции Swift, содержащей замыкание

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)") 
     } 
    }) 
} 
+0

Вы можете передать закрытие в качестве обратного вызова или обработчика завершения, который вы можете вызвать в том месте, где хотите вернуть элементы. Вам нужно написать, как вы готовы обрабатывать данные в закрытии, которые вы передаете функции loadData(). – iamyogish

ответ

0

Добавление обработчика завершения (названный 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, и она работает без ошибок:

enter image description here

+0

Ответ должен работать, но он выдает ошибки времени выполнения на игровой площадке (даже при включении перечисления структуры и ошибки). Затем я попытался сделать метод общедоступным в классе и вызвать его из моего контроллера представления. Нет ошибок, но вызов session.dataTaskWithURL не запускается. –

+0

Я добавил скриншот моей игровой площадки, на которой я отвечал без ошибок. Пожалуйста, смотрите. // Не забывайте, что для запуска асинхронного кода на игровой площадке вам нужно включить 'XCPSetExecutionShouldContinueIndefinitely'! :) – Moritz

+0

Извинения, забыли добавить XCPSetExecutionShouldContinueIndefinitely (true) –