Aloogy Aloogy - 5 months ago 83
Swift Question

Populating Grouped Table from Alamofire and SwiftyJSON

I've been attempting to populate a grouped table with data from an Alamofire request. I've so far managed to populate a table with static data from an array (as shown in the picture), but after hours of trying, looking up and experimenting, have still gotten no-where working out how to use the JSON data. It shouldn't make too much of a difference, but for the record this is in Swift 3.

Any help would be appreciated. Thanks.

Here's what the layout looks like

Here is my static code, which is working great.

import UIKit
import Alamofire
import SwiftyJSON

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

//static Data Here:
var array = [ ["Clients", "John Doe", "Joe Bloggs"],["Departments", "HR", "Admin", "Finance"]]
let cellReuseIdentifier = "cell"
@IBOutlet var tableView: UITableView!

override func viewDidLoad() {
tableView.delegate = self
tableView.dataSource = self
super.viewDidLoad()
}
func numberOfSections(in tableView: UITableView) -> Int {
return array.count
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return array[section].count - 1
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

let cell:AreasCustomCell = self.tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as! AreasCustomCell

cell.areasPreview.contentMode = .scaleAspectFit
request(.GET, "https://url.here.com", parameters: ["file": "default.png"]).response { (request, response, data, error) in
cell.areasPreview.image = UIImage(data: data!, scale:0.5)
}

cell.areasCellLabel.text = array[indexPath.section][indexPath.row + 1]
return cell
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return array[section][0]
}

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("You tapped cell number \(indexPath.row).")
//More things planned here later!
}
}


Here is also the format of the JSON I'm working with.

content = {
clients = (
{
desc = "Description here";
group = client;
id = "group_7jsPXXAcoK";
name = "John Doe";
},
{
desc = "Description here";
group = client;
id = "group_19MrV7OLuu";
name = "Joe Bloggs";
}
);
departments = (
{
desc = "Description here";
group = department;
id = "group_PhAeQZGyhx";
name = "HR";
},
{
desc = "Description here";
group = department;
id = "group_RMtUqvYxLy";
name = "Admin";
},
{
desc = "Description here";
group = department;
id = "group_T50mxN6fnP";
name = "Finance";
}
);
};
state = success;


I've so far added a new class to hold the JSON data which I believe is stepping in the right direction.

class Group {

let id : String
let name : String
let desc : String
let group : String

init(dictionary : [String : AnyObject]) {
id = dictionary["id"] as? String ?? ""
desc = dictionary["desc"] as? String ?? ""
name = dictionary["name"] as? String ?? ""
group = dictionary["group"] as? String ?? ""
}
}


And finally here is my function to get the JSON data in the first place which should be called from viewDidLoad.

func getData()
{
let defaults = UserDefaults.standard()
let token = defaults.string(forKey: defaultsKeys.userToken)
let email = defaults.string(forKey: defaultsKeys.userEmail)
request(.POST, "https://url.here.com/api/v2.php", parameters: ["type": "areas", "uEmail": email!, "token": token!])
.responseJSON { response in
var json = JSON(response.result.value!)
let state = json["state"].stringValue
if(state == "error"){
print(json["message"].stringValue)
} else {
print(response.result.value)
//Send JSON data here to Table!
}
}
}

Answer

Alright, when you receive a response from a request, the closure gives you back 3 values. example:

request(gibberish: DoesntMatter) {
    data, response, error in
}

you generally want to check the response for a 200 result like so

if let httpResponse = response as? NSHTTPURLResponse {
      if httpResponse.statusCode == 200 {
            //do something with data
      }
}

At this point, when working with swiftyJSON, you can get the data like so:

if let httpResponse = response as? NSHTTPURLResponse {
      if httpResponse.statusCode == 200 {
            let json = JSON(data: data)
      }
}

Now the best way to retrieve the json at this point is with a closure since API calls are done synchronously and we need to know when the response is finished.

    func performAPICall(url: NSURL, resultHandler: ((json: JSON) -> Void)) {

            let session = NSURLSession(configuration: .defaultSessionConfiguration())
            let tokenRequest = NSMutableURLRequest(URL: url)
            tokenRequest.HTTPMethod = "GET"

            let dataTask = session.dataTaskWithRequest(tokenRequest) {
                (let data, let response, let error) in
                if let httpResponse = response as? NSHTTPURLResponse {
                    if error == nil {
                          if httpResponse.statusCode == 200 {
                                let json = JSON(data: data!)
                                resultHandler(json)
                          } else {
                                print("Failed request with response: \(httpResponse.statusCode)")
                          }
                    } else {
                        print("Error during GET Request to the endpoint \(url).\nError: \(error)")
                    }
                }
            }
            dataTask.resume()
    }

Then you call the function and do what you like to with the data like so:

 performAPICall(url) {
              json in
          //this will have your full response
          print(json)
          //parse the json easily by doing something like
          var clientArray: [Group] = []

          let clients = json["clients"]
          for client in clients {
                var thisClient = Group()
                thisClient.id = json["id"].string
                thisClient.desc = json["desc"].string
                thisClient.name = json["name"].string
                //not quite sure how to store this one
                thisClient.group = json["group"].anyObject
                clientArray.setByAddingObject(thisClient)
          }
          //make sure to call tableView.reloadData() when you give the tableViews //it's value.
    }

You can do this through the init as well, but make sure you setup that function appropriately. Otherwise I would just initialize the values of your object to nil or empty. Also your JSON response is returning a value that is not a string. Make sure you mess with it to find a proper way of storing it. Hope this helps!

Comments