Showing JSON data on TableView using SwiftyJSON and Alamofire
Asked Answered
H

2

5

I want to show the JSON data grabbed from a server on a Table View. The problem is, I can't get it to show up on it. I have tried several different methods and searched a lot to find a solution, but I can't.

My code (all of it) is shown below and I hope somebody can help me out. Thanks in advance.

import UIKit
import Alamofire
import SwiftyJSON

class MasterViewController: UITableViewController {

var tableTitle = [String]()
var tableBody = [String]()

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    getJSON()

}

func getJSON(){

    Alamofire.request(.GET, "http://announcement.vassy.net/api/AnnouncementAPI/Get/").responseJSON { (Response) -> Void in

        // checking if result has value
        if let value = Response.result.value {

            let json = JSON(value)

            for anItem in json.array! {

                let title: String? = anItem["Title"].stringValue
                let body: String? = anItem["Body"].stringValue
                self.tableTitle.append(title!)
                self.tableBody.append(body!)

            }

        }

    }

    self.tableView.reloadData()

}

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

// Table View Stuff

override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    return 1
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return self.tableTitle.count
}


override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! TableViewCell

    // cell config
    cell.title!.text = tableTitle[indexPath.row]
    cell.body!.text = tableBody[indexPath.row]
    return cell

}

}
Hamil answered 15/3, 2016 at 18:52 Comment(0)
M
8

The Alamofire network request is asynchronous, meaning you can't know when the result will come back.

The problem here is that you reload the tableView outside the scope of the Alamofire request, so it is executed before the data comes back.

The reload should happen in the same scope, and on the main thread, for example:

func getJSON(){

    Alamofire.request(.GET, "http://announcement.vassy.net/api/AnnouncementAPI/Get/").responseJSON { (Response) -> Void in

        // checking if result has value
        if let value = Response.result.value {

            let json = JSON(value)

            for anItem in json.array! {

                let title: String? = anItem["Title"].stringValue
                let body: String? = anItem["Body"].stringValue
                self.tableTitle.append(title!)
                self.tableBody.append(body!)

            }

            dispatch_async(dispatch_get_main_queue()) {
                self.tableView.reloadData()             
            }

        }

    }

}
Milligan answered 15/3, 2016 at 19:4 Comment(0)
R
0

I think @Eric said almost everything in his answer, nevertheless, not it's a good decision in design keep the code for make the network request in your same UITableViewController this keep a couple between two things that are independents and change for differents reasons.

My advice is separate the two parts of the code decoupling the dependency between your two layers. In this way when you need to change anything related with your networking request handler you don't need to change it in any place where you make the same request, it's an advice!!!.

In case you want to do it, you can use closures to hanlde the async behaviour of Alamofire passgin the completionHandlerinside the wrapper you make to handle the networking requests, for example, let's define a simple wrapper using the singleton pattern (it's just for the purpose of explain the sample, you can handle it as you want).

import AlamofireImage
import SwiftyJSON

class NetworkHandler { 

   /// The shared instance to define the singleton.
  static let sharedInstance = RequestManager()


  /**
  Private initializer to create the singleton instance.
  */
  private init() { }

  func getJSON(completionHandler: (json: JSON?, error: NSError?) -> Void) {

      Alamofire.request(.GET, http://announcement.vassy.net/api/AnnouncementAPI/Get/).responseJSON { response in

        switch(response.result) {
        case .Success(let value):

            let json = JSON(value)
            completionHandler(json: json, error: nil)

        case .Failure(let error):
            completionHandler(json: nil, error: error)
        }
    }
  }
}

Then in your UITableViewController you can call the new wrapper to Alamofire in this way:

class MasterViewController: UITableViewController {

   var tableTitle = [String]()
   var tableBody = [String]()

   override func viewDidLoad() {
       super.viewDidLoad()
       // Do any additional setup after loading the view, typically from a nib.

       NetworkHandler.sharedInstance.getJSON { [weak self] (json, error) -> Void in

         // request was made successful
         if error == nil {
            for anItem in json.array! {
               let title: String? = anItem["Title"].stringValue
               let body: String? = anItem["Body"].stringValue
               self.tableTitle.append(title!)
               self.tableBody.append(body!)
            }

            dispatch_async(dispatch_get_main_queue()) {
                self.tableView.reloadData()             
            }
         }
       }
    }

    // rest of your code
 }

In the above way you keep the code decoupled, it's a good design.

I hope this help you

Rosinweed answered 15/3, 2016 at 20:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.