2016-11-08 111 views
0

我想將一個練習JSON加載到表視圖中,我有一個服務函數從一個源代碼獲取數據作爲JSON,並有一個視圖控制器和一個表,我想要加載信息到。代碼中沒有錯誤,但表格加載空白行,調試部分通過打印命令顯示JSON數據。我是一個初學者,所以我很確定我錯過了一個核心元素,但無法解決它!如何將JSON加載到TableView中?

API服務

class ApiService { 

static var swiftyJsonVar:JSON? 

class func getExerciseData() { 

    Alamofire.request("https://wger.de/api/v2/exercise/?format=json").responseJSON { (responseData) -> Void in 
     if((responseData.result.value) != nil) { 
      swiftyJsonVar = JSON(responseData.result.value!) 
      print(swiftyJsonVar ?? nil) 
     } 

    } 
} 

視圖控制器

class ExerciseDatabaseController: UIViewController, UITableViewDataSource, UITableViewDelegate { 

@IBOutlet weak var ExerciseSearchField: UISearchBar! 
@IBOutlet weak var ExercisesTableView: UITableView! 

var arrRes = [[String:AnyObject]]() // Array of dictionary 

override func viewDidLoad() { 
    super.viewDidLoad() 

    let arrRes = ApiService.getExerciseData() 

    if let resData = ApiService.swiftyJsonVar?["exercise"].arrayObject { 
     self.arrRes = resData as! [[String:AnyObject]] 
    } 
    if self.arrRes.count > 0 { 
     self.ExercisesTableView.reloadData() 
    } 

    print(arrRes) 
    // Do any additional setup after loading the view. 
} 

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 
    return arrRes.count 
} 

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 
    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) 
    var dict = arrRes[indexPath.row] 
    cell.textLabel?.text = dict["name"] as? String 
    cell.detailTextLabel?.text = dict["description"] as? String 
    return cell 
} 
+1

您的網絡電話是異步的。這是你的問題。當你收到數據時,重新加載tableView。在'getExerciseData()'上使用閉包。 – Larme

回答

1

你應該異步加載你的JSON,這意味着你應該在,讓您的alamofire調用方法關閉。

class ApiService { 

class func getExerciseData(completion: @escaping ([[String: AnyObject]]) ->()) { 

    Alamofire.request("https://wger.de/api/v2/exercise/?format=json").responseJSON { (responseData) -> Void in 
     guard let jsonResponse = responseData.result.value else { 
     //possibly put some sort of protection, or what you want to do if there is not a response here 
     return 
     } 
     //instead of creating a variable for swiftyJsonVar in this class, 
     //you want to use a completion to send the array of dictionaries to the tableview asynchronously, 
     //that way it doesn't load blank 
     //I'm not super familiar with swifty json(sorry). What I normally do is below. 
     let swiftyJsonVar = JSON(jsonResponse) 
     guard let dictArray = swiftyJsonVar["exercise"].arrayObject as? [[String: AnyObject]] else { 
     //some sort of protection here if this fails 
     return 
     } 
     completion(dictArray) 
    } 
} 

所以,現在我們已經取得了我們的異步調用(通常要做到這一點,只要你在視覺上,這不是已經預裝了互聯網電話/應用程序中保存的地方顯示信息)。

接下來,我們希望在tableview加載時在tableview中顯示這些信息。

class ExerciseDatabaseController: UIViewController, UITableViewDataSource, UITableViewDelegate { 
    //these should start with lower cases(exerciseSearchField), never uppercased 
    @IBOutlet weak var ExerciseSearchField: UISearchBar! 
    @IBOutlet weak var ExercisesTableView: UITableView! 

    var arrRes = [[String:AnyObject]]() // Array of dictionary 

    override func viewDidLoad() { 
     super.viewDidLoad() 
     //you said you would use these delegates up top when you created the class, so you have to set them 
     ExercisesTableView.delegate = self 
     ExercisesTableView.dataSource = self 
     fetchData() 
     // Do any additional setup after loading the view. 
    } 

    //this method will make the api call 
    //you'll notice that if you set breakpoints, it will reach the end of the method before hitting self?.arrRes = dictArray 
    //this is normal and how asynchronous calls work, look into tableview threading for a deeper explanation of why that is. It is super important to understand threading in iOS 
    //once it gets data back from the API call, it will go to the main thread and tell the tableview to reload with that data 

    func fetchData() { 
     ApiService.getExerciseData { [weak self] (dictArray) in 
     self?.arrRes = dictArray 
     print(self?.arrRes) 
     if self?.arrRes.count > 0 { 
      DispatchQueue.main.async { 
      self?.ExercisesTableView.reloadData() 
      } 
     } 
     } 
    } 

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 
     return arrRes.count 
    } 

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 
     let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) 
     var dict = arrRes[indexPath.row] 
     cell.textLabel?.text = dict["name"] as? String 
     cell.detailTextLabel?.text = dict["description"] as? String 
     return cell 
    } 

你會看到我在上面使用[weak self]。欲瞭解更多的爲什麼是必要的異步網絡電話的解釋/無論何時使用閉包,你可以在這裏閱讀: http://krakendev.io/blog/weak-and-unowned-references-in-swift

有很多其他資源的閱讀弱和強引用/父子東西在iOS版用快速的谷歌搜索。並且,在iOS中追求異步/同步的研究。這兩個主題在開始學習時非常重要。

+0

我嘗試使用此代碼時遇到以下錯誤,但它看起來正確嗎? 1.無法將類型'JSON'的值轉換爲期望的參數類型'[[String:AnyObject]]'2.在閉包中引用屬性'arrRes'需要明確的'self'。明確捕獲語義3.可選類型'Int?'的值不打開;你的意思是使用'!'要麼 '?'? 4.類型'ExerciseDatabaseController'的值沒有成員'ExerciseTableView' – infernouk

+0

現在修復一些東西,一會兒。 –

+0

如果你看看API調用,我們需要說明JSON響應是以字典數組的形式返回的。你會看到我添加爲? [[String:AnyObject]]。只要答案是這種格式,那就應該有效。如果這不起作用,請給我一個折點,然後爲您提供JSON響應,以便我可以查看它。 arrRes的事情相當簡單,只需添加自我?在它之前。如果你點擊它,編譯器警告可能會爲你做。沒有成員的事是因爲在ExercisesTableView.reload數據中的拼寫錯誤。我寫了ExerciseTableView(忘了s) –

0

刷新您的tableView一旦收到來自異步請求的JSON數據。所以,你的

self.ExercisesTableView.reloadData() 

將裏面去

Alamofire.request("https://wger.de/api/v2/exercise/?format=json").responseJSON { (responseData) -> Void in 
    if((responseData.result.value) != nil) { 
     swiftyJsonVar = JSON(responseData.result.value!) 
     print(swiftyJsonVar ?? nil) 
    } 
} 
+0

但是,如果我把它放在Alamofire請求中,那麼它會在不同的範圍內,並且不會看到ExercisesTableView能夠重新加載它? – infernouk

+0

你爲什麼要在不同的地方提出要求? – SaylorTwift2

+0

,因爲對於代碼庫的可擴展性,您不希望將您的服務放入viewcontrollers中,請參閱Phil Hudsons上面的響應 – infernouk

相關問題