我正在尝试将一个名为detail
的对象从一个名为RestManager.swift的swift文件传递给一个ViewController。该对象包含所有元素,但是当我在视图控制器中调用它时,它是空的。根据我在网上收集的内容,它可能与使用后台线程的URLSession有关
我的RestManager.swift看起来像这样。
class RestManager {
func reqDetails(id: Int) {
// Create URL
let id = String(id)
let url = "https://website.example.com/"
let url = URL(string: url + id)!
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil
{
print ("ERROR")
}
else
{
if let content = data
{
let jsonData = JSON(data: content)
let id = jsonData["id"].intValue
let name = jsonData["title"]["rendered"].string!
let link = jsonData["link"].url!
let content = jsonData["content"]["rendered"].string!
// Create Object
let detail = Detail(id: id, name: name, content: content, thumbnailUrl: link)
self.details.append(detail)
}
}
}
task.resume()
}
}
我的视图控制器如下所示:
class DetailViewController: UIViewController {
var ListingID = Int()
let restManager = RestManager()
@IBOutlet weak var ContentLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
restManager.reqDetails(id: ListingID)
ContentLabel.text? = restManager.details[0].name // After running the app this index value is out of range.
}
..
}
答案 0 :(得分:2)
使用close in函数传递这样的数据
func reqDetails(id: Int,completionHandler:@escaping (_ detilObject:Detail)->Void) {
// Create URL
let id = String(id)
let url = "https://website.example.com/"
let url = URL(string: url + id)!
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil
{
print ("ERROR")
}
else
{
if let content = data
{
let jsonData = JSON(data: content)
let id = jsonData["id"].intValue
let name = jsonData["title"]["rendered"].string!
let link = jsonData["link"].url!
let content = jsonData["content"]["rendered"].string!
// Create Object
let detail = Detail(id: id, name: name, content: content, thumbnailUrl: link)
self.details.append(detail)
completionHandler(self.details)
}
}
}
task.resume()
}
并像这样调用你的函数。 “
restManager.reqDetails(id: ListingID , completionHandler: { (detail) in
// here is your detail object
})
答案 1 :(得分:0)
您已经纠正了您的问题是由于尝试立即阅读回复(因为它不太可能立即就绪)。当数据准备就绪(或发生错误)时,您需要一种方法来通知您的控制器(在主线程/队列上!)。
一个简单但不优雅的解决方案是将weak var controller: DetailViewController
添加到RestManager
(弱,因此它不会导致保留周期)init
}(RestManager(controller: self)
)并在URL任务闭包中使用该引用来告诉控制器它已准备好或出错。
您应该使用通知或委托模式,以避免RestManager
与DetailViewController
的紧密耦合。
答案 2 :(得分:0)
您好我认为您刚刚遇到异步编程的典型陷阱。你所做的是在从URLSession返回之前询问任务的返回值。 您可以通过自己制作一个完成处理程序来解决这个问题,例如我从Darksky获取一些天气数据。 请注意,你甚至不需要为此创建一个类,只是一个函数。
PS注意我使用Alamofire,SwiftyJSON和Gloss,它使用REST接口大大降低了复杂性。这是Swift 3!
import Alamofire
import SwiftyJSON
import Gloss
typealias DarkSkyWeatherForecast = ( _ json : Gloss.JSON?, _ error : Error?) -> Void
func getWeatherForcast( latitude:Double, longitude:Double, completionHandler:@escaping DarkSkyWeatherForecast) -> Void {
let urlString = "https://api.darksky.net/forecast/"+darkSkyKey+"/"+String(latitude)+","+String(longitude)+"?units=si"
Alamofire.request(urlString).responseJSON { (response) in
if let resp = response.result.value {
let json = JSON(resp)
let glossJson = json.dictionaryObject
completionHandler( glossJson, nil)
}else{
completionHandler( nil, response.error)
}
}
}
并且调用函数如下:
getWeatherForcast(latitude: lat, longitude: lon) { (jsonArg, error) in
if error == nil{
guard let weather = DarkSkyWeather(json: jsonArg!) else {
self.effectView.removeFromSuperview()
return
}
if let ambTemp = weather.currently?.temperature, let windspd = weather.currently?.windSpeed, let windDir = weather.currently?.windBearing{
self.effectView.removeFromSuperview()
self.ambTemperature.text = String(ambTemp)
self.windSpeed.text = String( windspd )
self.windDirection.text = String( windDir )
self.getSpeed()
}
}else{
self.effectView.removeFromSuperview()
let alert = UIAlertController(title: "Error", message: "Could not get weather forecast", preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default, handler: { (action) in
self.dismiss(animated: true, completion: nil)
})
alert.addAction(okAction)
self.present(alert, animated: true, completion: nil)
}
}
注意,我只在完成处理程序完成后填充文本字段,并实际返回一些数据或错误。忽略“effectView”的东西,这是一个特殊的光标等待微调器: - )
而且要知道这些映射到json数据的结构可能会有所帮助:
//common struct for weather data
public struct WeatherDataStruct : Decodable{
let time : Int?
let summary : String?
let icon : String?
let precipIntensity : Double?
let precipProbability : Double?
let precipType : String?
let temperature : Double?
let apparentTemperature : Double?
let dewPoint : Double?
let humidity: Double?
let windSpeed : Double?
let windBearing : Int?
let visibility : Double?
let cloudCover : Double?
let pressure : Double?
let ozone : Double?
public init?( json: JSON){
self.time = "time" <~~ json
self.summary = "summary" <~~ json
self.icon = "icon" <~~ json
self.precipIntensity = "precipIntensity" <~~ json
self.precipProbability = "precipProbability" <~~ json
self.precipType = "precipType" <~~ json
self.temperature = "temperature" <~~ json
self.apparentTemperature = "apparantTemperature" <~~ json
self.dewPoint = "dewPoint" <~~ json
self.humidity = "humidity" <~~ json
self.windSpeed = "windSpeed" <~~ json
self.windBearing = "windBearing" <~~ json
self.visibility = "visibility" <~~ json
self.cloudCover = "cloudCover" <~~ json
self.pressure = "pressure" <~~ json
self.ozone = "ozone" <~~ json
}
}
//hourly weather struct
public struct HourlyStruct : Decodable{
let summary : String?
let icon : String?
let data : [WeatherDataStruct]?
public init?(json: JSON) {
self.summary = "summary" <~~ json
self.icon = "icon" <~~ json
self.data = "data" <~~ json
}
}
//total struct for the whole json answer from darksky weather
public struct DarkSkyWeather : Decodable{
let latitude : Double?
let longitude : Double?
let timezone : String?
let offset : Int?
let currently : WeatherDataStruct?
let hourly : HourlyStruct?
public init?(json: JSON) {
self.latitude = "latitude" <~~ json
self.longitude = "longitude" <~~ json
self.timezone = "timezone" <~~ json
self.offset = "offset" <~~ json
self.currently = "currently" <~~ json
self.hourly = "hourly" <~~ json
}
}