将对象从 URLSession 传递到 ViewController
Passing object from URLSession to ViewController
我正在尝试将我创建的名为 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.
}
..
}
您是对的,您的问题是由于尝试立即读取响应(因为它不太可能立即准备好)。当您的数据准备就绪(或发生错误)时,您需要一种方法来通知您的控制器(在主要 thread/queue!)。
一个简单但不优雅的解决方案是将 weak var controller: DetailViewController
添加到传递给 init
的 RestManager
(较弱,因此不会导致保留周期)(RestManager(controller: self)
) 并在 URL 任务闭包中使用该引用来告诉控制器它已经准备好或出错了。
您可能应该使用通知或委托模式来避免 RestManager
与 DetailViewController
的紧密耦合。
在函数中使用closer来像这样传递数据
'
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
})
'
您好,我认为您刚刚遇到了典型的异步编程陷阱。您所做的是在任务从 URLSession 返回之前请求任务的 return 值。
你可以通过自己制作一个完成处理程序来解决它,就像这个例子一样,我从 Darksky 获得了一些天气数据。
请注意,您甚至不需要为此创建一个 class,只需一个函数即可。
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)
}
}
注意,我只在完成处理程序完成后才填充文本字段,实际上 returns 一些数据或错误。忽略 "effectView" 的东西,那是一个特殊的 curser wait spinner :-)
并且了解映射到 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
}
}
我正在尝试将我创建的名为 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.
}
..
}
您是对的,您的问题是由于尝试立即读取响应(因为它不太可能立即准备好)。当您的数据准备就绪(或发生错误)时,您需要一种方法来通知您的控制器(在主要 thread/queue!)。
一个简单但不优雅的解决方案是将 weak var controller: DetailViewController
添加到传递给 init
的 RestManager
(较弱,因此不会导致保留周期)(RestManager(controller: self)
) 并在 URL 任务闭包中使用该引用来告诉控制器它已经准备好或出错了。
您可能应该使用通知或委托模式来避免 RestManager
与 DetailViewController
的紧密耦合。
在函数中使用closer来像这样传递数据
'
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
})
'
您好,我认为您刚刚遇到了典型的异步编程陷阱。您所做的是在任务从 URLSession 返回之前请求任务的 return 值。 你可以通过自己制作一个完成处理程序来解决它,就像这个例子一样,我从 Darksky 获得了一些天气数据。 请注意,您甚至不需要为此创建一个 class,只需一个函数即可。
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)
}
}
注意,我只在完成处理程序完成后才填充文本字段,实际上 returns 一些数据或错误。忽略 "effectView" 的东西,那是一个特殊的 curser wait spinner :-)
并且了解映射到 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
}
}