2016-11-08 32 views
0

第一篇文章,請溫和。我在Swift 3,Xcode 8.1中玩弄了JSON數據。我特別從http://forecast.weather.gov/MapClick.php?lat=38.9782&lon=-76.4933&FcstType=json獲取我的數據。我的代碼如下:無法從Swift 3的dataTask中獲取JSON數據

private func getWeatherData(url:URL) -> (currentObservation:Dictionary<String, AnyObject>, currentData:Dictionary<String, AnyObject>) { 
    var currentObservation:Dictionary = [:] as Dictionary<String, AnyObject> 
    var currentData:Dictionary = [:] as Dictionary<String, AnyObject> 
    let task = URLSession.shared.dataTask(with: url) { (data, response, error) in 
     if error != nil { 
      print ("Error retrieving URLSession") 
     } 
     else 
     { 
      if let content = data 
      { 
       do 
       { 
        let JSONData = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject 

        if let observation = JSONData["currentobservation"] as? Dictionary<String, AnyObject> { 
         currentObservation = observation 
         print("currentObservation inside of task: \(currentObservation)\n\n") 
        } 

        if let data = JSONData["data"] as? Dictionary<String, AnyObject> { 
         currentData = data 
         print("currentData inside of task: \(currentData)") 
        } 

       } 
       catch 
       { 
        print("Error in trying JSONSerialization") 

       } 

      } 
     } 
    } 

    task.resume() 

    print("currentObservation outside of task: \(currentObservation) \n\n") 
    print("currentData outside of task: \(currentData)") 

    return (currentObservation , currentData) 

} 

我得到的輸出是:

currentObservation outside of task: [:] 


currentData outside of task: [:] 
currentObservation: [:] 


currentData: [:] 
currentObservation inside of task: ["state": MD, "name": Annapolis,  United States Naval Academy, "longitude": -76.49, "elev": 3, "Relh": 79, "WindChill": NA, "Weather": Fair, "Altimeter": 1028.7, "latitude": 38.99, "Temp": 46, "SLP": 30.37, "id": KNAK, "timezone": EST, "Weatherimage": nsct.png, "Windd": 0, "Winds": 0, "Visibility": 10.00, "Gust": 0, "Dewp": 40, "Date": 7 Nov 20:54 pm EST] 


currentData inside of task: ["hazard": <__NSArrayM 0x60800005a520>(
Frost Advisory, Hazardous Weather Outlook), "weather": <__NSArrayM 0x60800005a490>(
Clear then Areas Frost, Areas Frost then Sunny, 
Partly Cloudy then Slight Chance Showers, etc.(edited for brevity)] 

我得到一個JSON數據的反應,但我似乎無法保持數據的保持。

我很困惑的幾件事情。首先,爲什麼URLSession.shared.dataTask之外的打印語句在打印語句之前打印?

此外,「currentObservation」和「currentData」的打印語句在函數外調用,並且它們打印在URLSession.shared.dataTask中的打印語句之前。這是爲什麼?

最後,最重要的是,爲什麼JSON數據沒有超出URLSession.shared.dataTask塊呢?

任何洞察將有所幫助。謝謝。

編輯: 這是我現在的代碼基於維諾德的迴應。當我打印它們時,currentObservation和currentData仍然是空的。

class ViewController: UIViewController { 

override func viewDidLoad() { 
    super.viewDidLoad() 
    var currentObservation:Dictionary = [:] as Dictionary<String, AnyObject> 
    var currentData:Dictionary = [:] as Dictionary<String, AnyObject> 

    let serverURL = URL(string:"http://forecast.weather.gov/MapClick.php?lat=38.9782&lon=-76.4933&FcstType=json") 
    getWeatherData(serverURL: serverURL!, completion: { serverResponse in 
     if let observation = serverResponse["currentobservation"] as? Dictionary<String, AnyObject> { 
      currentObservation = observation 
      print("currentObservation inside of task: \(currentObservation)\n\n") 
     } 

     if let data = serverResponse["data"] as? Dictionary<String, AnyObject> { 
      currentData = data 
      print("currentData inside of task: \(currentData)\n\n") 
     } 
    }) 

    print("currentObservation outside of task: \(currentObservation)\n\n") 
    print("currentData outside of task: \(currentData)") 



} 

override func didReceiveMemoryWarning() { 
    super.didReceiveMemoryWarning() 
    // Dispose of any resources that can be recreated. 
} 


private func getWeatherData(serverURL:URL, completion:@escaping (AnyObject) ->()){ 
    let task = URLSession.shared.dataTask(with: serverURL) { (data, response, error) in 
     if error != nil { 
      print ("Error retrieving URLSession") 
     } 
     else 
     { 
      if let content = data 
      { 
       do 
       { 
        let JSONData = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject 

        completion(JSONData) 

       } 
       catch 
       { 
        print("Error in trying JSONSerialization") 

       } 

      } 
     } 
    } 

    task.resume() 


} 

}

是的,我知道我不應該在所有的視圖控制器違反了MVC的可以這樣做,但我只是玩弄的努力得到它添加的複雜工作之前數據模型和控制器以及UI。謝謝。

+0

'dataTask'以異步方式工作。您不能從包含異步任務的方法返回任何內容。 – vadian

+0

我想這是有道理的。如果在函數調用完成時已完成採樣,那麼如何返回任何內容。謝謝。 – Yrb

+0

Vinod的答案提供了一種使用回調閉包異步返回數據的解決方案。此外,在這裏有幾百個相關的主題。 – vadian

回答

0

首先,URLSession.shared.dataTask閉包內的代碼實際上並未在主線程中執行,一旦您調用task.resume()它將會被調用,並且它將在後臺運行,因此您在其外部執行的所有打印都將首先被調用,因爲這task還沒有完成,這就是爲什麼打印currentObservation inside of task裏面會後

二被調用,你不境外使用任務的響應代碼塊的,試想這樣的:創建任務 - >恢復任務 - >網絡請求.... - >完成代碼關閉,您必須執行代碼,包含完成代碼關閉中的響應,這是持有打印的部分currentObservation inside of task

+0

感謝您的解釋。我認爲後臺線程必須專門調用。我始終打算最終在後臺提出請求。當然,這將解釋時間。這當然會導致如何在封閉之外使用響應的問題。 – Yrb

+0

你不這樣做,當然你可以保存它作爲屬性和調用方法或任何東西,但都必須從內部開始結束關閉,因爲那是你得到的迴應,而不是從其他地方 – Tj3n

0

你的關閉不好。請改變如下,以獲得適當的迴應。

服務呼叫方法:

private func getWeatherData(serverURL:URL ,completion:@escaping (AnyObject) ->()){ 
      let task = URLSession.shared.dataTask(with: serverURL) { (data, response, error) in 
       if error != nil { 
        print ("Error retrieving URLSession") 
       } 
       else 
       { 
        if let content = data 
        { 
         do 
         { 
          let JSONData = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject 

          completion(JSONData)   

         } 
         catch 
         { 
          print("Error in trying JSONSerialization") 

         } 

        } 
       } 
      } 

      task.resume() 


     } 

響應解析:

var currentObservation:Dictionary = [:] as Dictionary<String, AnyObject> 
     var currentData:Dictionary = [:] as Dictionary<String, AnyObject> 

     let serverURL = URL(string:"http://forecast.weather.gov/MapClick.php?lat=38.9782&lon=-76.4933&FcstType=json") 
     getWeatherData(serverURL: serverURL!, completion: { serverResponse in 
      if let observation = serverResponse["currentobservation"] as? Dictionary<String, AnyObject> { 
       currentObservation = observation 
       print("currentObservation inside of task: \(currentObservation)\n\n") 
      } 

      if let data = serverResponse["data"] as? Dictionary<String, AnyObject> { 
       currentData = data 
       print("currentData inside of task: \(currentData)") 
      } 
     }) 
+0

因爲我很明顯關閉不夠強大,我不得不查看逃逸關閉。這顯然比嘗試將信息作爲返回值傳遞回來要優雅得多。我沒有得到,僅僅爲了我自己的知識,就是爲什麼將它作爲回報值傳遞不起作用。你能解釋一下嗎?謝謝。 – Yrb

+0

我嘗試了代碼,但它仍然無法正常工作。我懷疑這個問題是因爲封閉性質作爲後臺線程。當我查看變量時,閉包沒有逃脫,因此它們仍然是空的。所以,我的問題是如何對它進行編碼,以便在查看它們之前等待變量填充完成。我將發佈我的整個代碼,但響應數據響應仍然相同。 – Yrb

+0

@Yrb它沒有返回值的原因是因爲在返回行被命中時該值不存在。 'dataTask'是一個異步任務。也就是說,它將在不同線程的後臺運行。所以發生的事情的順序是...... 1.開始任務。 2.到達函數的結尾並返回你的值(它仍然是'[:]')3.任務完成並進入完成關閉。 4.解析JSON。 5.賦值給'currentObservation'和'currentData'。正如你在這裏看到的。事情發生的順序並不是代碼中寫入行的順序。 – Fogmeister