2017-06-24 79 views
1

我試圖將一個名爲RestManager.swift的swift文件中創建的名爲detail的對象傳遞給ViewController。該對象包含所有元素,但當我在視圖控制器中調用它時,它是空的。從我在線上收集到的信息可能與URLSession在後臺線程上工作有關將URLSession中的對象傳遞給ViewController

我的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. 


} 

.. 

} 

回答

3

使用在功能上更接近數據傳遞這樣

'

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

0

你是正確的,你的問題是由於試圖立即讀取響應(因爲它是極不可能準備好立即)。當數據準備好(或發生錯誤)時,您需要一種方式通知您的控制器(在主線程/隊列上!)。

一個簡單的,如果不雅的解決辦法是增加一個weak var controller: DetailViewControllerRestManager(弱,因此不會引起保留週期)這是傳遞initRestManager(controller: self)),並在URL中使用任務關閉,提及告訴控制器它已經準備好或出錯了。

儘管您應該使用通知或代理模式,以避免RestManagerDetailViewController的緊密耦合。

+0

您可以使用授權流程以您的'details'數組作爲您的視圖控制器中的參數獲取回調。現在看看如何在swift中使用協議實現委託。 – Tann

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 
    } 
} 
相關問題