5

I need to build Arrays for a Grouped UITableView, with a Title and Detail line in each table cell. I've got my json output from the server putting it in the right shape to iterate through the UITableViewDataSource methods. But what's the simplest way to convert these to a readable array that those UITableView functions can reference?

The headings array is for the Group headings, so it's just a one-dimensional array. I can iterate that. The titles and details arrays are both two dimensional. I can't figure out how to do that in Swift.

"headings":["Tuesday, August 16, 2016","Wednesday, August 17, 2016","Thursday, August 18, 2016","Friday, August 19, 2016","Saturday, August 20, 2016","Sunday, August 21, 2016","Monday, August 22, 2016","Tuesday, August 23, 2016","Wednesday, August 24, 2016","Thursday, August 25, 2016","Friday, August 26, 2016","Saturday, August 27, 2016","Sunday, August 28, 2016","Monday, August 29, 2016","Tuesday, August 30, 2016","Wednesday, August 31, 2016","Thursday, September 1, 2016","Friday, September 2, 2016","Saturday, September 3, 2016","Sunday, September 4, 2016","Monday, September 5, 2016","Tuesday, September 6, 2016","Wednesday, September 7, 2016","Thursday, September 8, 2016","Friday, September 9, 2016","Saturday, September 10, 2016","Sunday, September 11, 2016","Monday, September 12, 2016","Tuesday, September 13, 2016","Wednesday, September 14, 2016","Thursday, September 15, 2016","Friday, September 16, 2016"],

"titles":[["Joe Johnson"],["Joe Johnson"],["Sandy Primmell","Joe Johnson"],["Joe Johnson"],["Joe Johnson"],["Joe Johnson"],["Joe Johnson"],["Sandy Primmell","Joe Johnson"],["Joe Johnson","Joe Johnson"],["Sandy Primmell","Joe Johnson"],["Mark Greene","Joe Johnson"],["Joe Johnson"],["Joe Johnson"],["Joe Johnson"],["Joe Johnson"],["Sandy Primmell","Joe Johnson"],["Joe Johnson"],["Sandy Primmell","Joe Johnson"],["Mark Greene","Joe Johnson"],["Joe Johnson"],["Joe Johnson"],["Joe Johnson"],["Joe Johnson"],["Joe Johnson"],["Joe Johnson"],["Joe Johnson"],["Joe Johnson"],["Joe Johnson"],["Joe Johnson"],["Joe Johnson"],["Joe Johnson"],["Joe Johnson"]],

"details":[["OFF"],["OFF"],["Gregory","OFF"],["Gregory"],["OFF"],["OFF"],["OFF"],["Weekday Rounders","OFF"],["Weekday Rounders","Night Owls"],["Gregory","OFF"],["Gregory","OFF"],["OFF"],["OFF"],["OFF"],["Gregory"],["Gregory","OFF"],["Gregory"],["Gregory","OFF"],["Gregory","OFF"],["OFF"],["OFF"],["OFF"],["OFF"],["OFF"],["OFF"],["OFF"],["OFF"],["OFF"],["OFF"],["OFF"],["OFF"],["OFF"]]

UPDATE

Here's my Alamofire async function that grabs the data:

manager.request(.POST, getRouter(), parameters:["dev": 1, "app_action": "schedule", "type":getScheduleType(), "days_off":getScheduleDaysOff(), "period":getSchedulePeriod(), "begin_date":getScheduleBeginDate(), "end_date":getScheduleEndDate()])
        .responseString {response in
            print(response)
            var json = JSON(response.result.value!);
// what I'm missing
   }
3
  • how did you get that data from server? some framework have built in JSON parser that will allow you to easily create those array Commented Aug 16, 2016 at 12:18
  • @Thom try my answer and let me know if you have some problems Commented Aug 16, 2016 at 12:20
  • @Thom ok, if it works for you mark my answer as right, I would appreciate it, thank you Commented Aug 16, 2016 at 12:22

4 Answers 4

4

You can use this function:

func convertStringToDictionary(text: String) -> [String:AnyObject]? {
    if let data = text.dataUsingEncodi‌​ng(NSUTF8StringEncodi‌​ng) {
        do {
            return try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [String:AnyObject]
        } catch let error as NSError {
            print(error)
        }
    }
    return nil
}

and then you can read the array like this:

if let dict = convertStringToDictionary(jsonText) {
    let array = dict["headings"] as? [String]
}
Sign up to request clarification or add additional context in comments.

6 Comments

I put the function into a Swift file and it gave me two errors: On line 2: Value of type 'String' has no member 'data' On line 4: Use of unresolved identifier 'JSONSerialization'
@Thom try this: let data = text.dataUsingEncoding(NSUTF8StringEncoding)
That fixed the first error. Still getting the one on line 4.
@Thom replace JSONSerialization with NSJSONSerialization
I tried that but then it said that jsonObject is not a member of NSJSONSerialization
|
2

Alternatively, You can use JSON parsing libraries like Argo or SwiftyJSON, which were created to simplify the JSON parsing. They are both well tested and will handle edge cases for you, like missing parameters in the JSON responses etc.

An example using Argo:

Assuming the JSON response has this format (from Twitter API)

{
  "users": [
    {
      "id": 2960784075,
      "id_str": "2960784075",
      ...
    }
}

1- Create a Swift class to represent the response

Note that Response is a class that contains an array of User which is another class not shown here, but you get the point.

struct Response: Decodable {
    let users: [User]
    let next_cursor_str: String

    static func decode(j: JSON) -> Decoded<Response> {
        return curry(Response.init)
            <^> j <|| "users"
            <*> j <| "next_cursor_str"
    }
}

2- Parse the JSON

//Convert json String to foundation object
let json: AnyObject? = try? NSJSONSerialization.JSONObjectWithData(data, options: [])

//Check for nil    
if let j: AnyObject = json {
  //Map the foundation object to Response object
  let response: Response? = decode(j)
}

An example using Swifty

As explained in the official documentation:

1- Convert JSON string to SwiftyJSON object

if let dataFromString = jsonString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false) {
    let json = JSON(data: dataFromString)
}

2- Access specific element

If data is an array then use the index

//Getting a double from a JSON Array
let name = json[0].double

If data is a dictionary then use the key

//Getting a string from a JSON Dictionary
let name = json["name"].stringValue

2'- Loop over the elements

Array

//If json is .Array
//The `index` is 0..<json.count's string value
for (index,subJson):(String, JSON) in json {
    //Do something you want
}

Dictionary

//If json is .Dictionary
for (key,subJson):(String, JSON) in json {
   //Do something you want
}

4 Comments

Thanks. How about with Swifty? I looked there but couldn't successfully implement anything.
@Thom Referenced the important pieces from the documentation. Notice that with Argo you create a class and then let Argo map the JSON data to your class, but in Swifty, you don't create a class, but you do the mapping yourself.
This is terrific. Thank you. I am using SwiftyJson and was able to use it to iterate through the one-dimensional one no problem. Is a dictionary basically the JS object equiv?
You mean equivalent to the JS dictionary? Yes, it is :)
2

I would suggest using AlamofireObjectMapper. The library lets you you map objects from json easily and if combined with Alamofire can cast and return your object on the server response. The object mapping itself should look like this in your case

class CustomResponseClass: Mappable {
    var headings: [String]?

    required init?(_ map: Map){

    }

    func mapping(map: Map) {
        headings <- map["headings"]
   }
}

This way you decouple the logic of mapping and parsing json from your tableViewController.

AlamofireObjectMapper

3 Comments

Thanks. Checking this out.
@Thom In case you found your solution can you please accept one of the above answers please.
I used bits and pieces from multiple answers but I chose the one that I think was the closest to my final solution.
2

Looks like you are getting Dictionary from json and each key contain Array, you can try something like this, first declare one Dictionary instance and use that with TableViewDataSource methods.

var response = [String: AnyObject]()

do {
     self.response = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as! [String: AnyObject]
     print(dic)
}
catch let e as NSError {
     print(e.localizedDescription)
}

Now inside tableView methods

func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    if let arr = self.response["headings"] as? [String] {
        return arr.count
    }
    return 0
}

func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    let headings = self.response["headings"] as! [String]
    return headings[Int]
}

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    if let arr = self.response["titles"] as? [[String]] {
        return arr[Int].count
    }
    return 0
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let titles = self.response["titles"] as! [[String]]
    let details = self.response["details"] as! [[String]]       
    let cell = tableView.dequeueReusableCellWithIdentifier("cell") as! EmployeeCell
    cell.mainLabel?.text = titles[indexPath.section][indexPath.row]
    cell.detailLabel?.text = details[indexPath.section][indexPath.row]
    return cell
}

11 Comments

What am I putting in data!? I'm getting errors in your do/catch.
In your api call you are getting NSData object, that you need to pass with that method, If you don't get it add the code in question that you have used to call api.
after this line var json = JSON(response.result.value!); add line like this self.response = json as! [String: AnyObject]` and then reload the tableView.
Have you try like i said?
Are you saying that I don't need the do/catch?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.