问题
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
}
}
回答1:
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()
}
}
}
}
回答2:
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 completionHandler
inside 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
来源:https://stackoverflow.com/questions/36019725/showing-json-data-on-tableview-using-swiftyjson-and-alamofire