2

我正在尝试将我创建的对象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.


}

..

}
4

3 回答 3

3

使用 close 函数来传递这样的数据

'

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
            })

'

于 2017-06-25T02:06:21.627 回答
0

您是正确的,您的问题是由于尝试立即阅读响应造成的(因为它不太可能立即准备好)。当您的数据准备好(或发生错误)时,您需要一种方法来通知您的控制器(在主线程/队列上!)。

一个简单但不优雅的解决方案是将 a 添加weak var controller: DetailViewController到传递给( ) 的RestManager(弱,因此它不会导致保留循环),并在 URL 任务闭包中使用该引用来告诉控制器它已准备好或出错。initRestManager(controller: self)

您可能应该使用通知或委托模式来避免 to 的紧密RestManager耦合DetailViewController

于 2017-06-24T22:32:26.727 回答
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)
            }
        }

请注意,我仅在完成处理程序完成并实际返回一些数据或错误后才填充文本字段。忽略“效果视图”的东西,这是一个特殊的光标等待微调器:-)

并且了解这些映射到 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
    }
}
于 2017-06-25T11:04:57.087 回答