-2

My scenario, I am trying to load JSON data into UITableView. Here, the problem is my JSON having multiple array of multiple values. I need to get array keys as a Tableview section names and its all values load into relevant cell. I am using codable method for easy JSON data process. Now, how to do array key names (School, Office, etc,) into section and its values relevant cell.

My JSON

https://api.myjson.com/bins/r763z

My Codable

   struct Root : Decodable {
        let status : Bool
        let data: ResultData
    }

    struct ResultData : Decodable {
        let school, college, office, organisation, central : [Result]
    }

    struct Result : Decodable {
        let id, name, date : String
        let group : [String]
    }

My JSON Decoder Code

func loadJSON(){

        let urlPath = "https://api.myjson.com/bins/r763z"
        let url = NSURL(string: urlPath)
        let session = URLSession.shared
        let task = session.dataTask(with: url! as URL) { data, response, error in
            guard data != nil && error == nil else {
                print(error!.localizedDescription)
                return
            }
            do {

                let decoder = JSONDecoder()
                self.tableData = try decoder.decode(DivisionData.self, from: data!) // How to get section values and cell values and load table data
                DispatchQueue.main.async {

                    self.tableView.reloadData()

                }

            } catch { print(error) }
        }
        task.resume()
    }

Expected Output

Soniya
  • 1
  • 5

2 Answers2

-1

Just use the different arrays for the different sections.

var tableData: ResultData?

override func numberOfSections(in tableView: UITableView) -> Int {
    return 5
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    guard let tableData = tableData else { return 0 }
    switch section {
    case 0:
        return tableData.school.count
    case 1:
        return tableData.college.count
    case 2:
        return tableData.office.count
    case 3:
        return tableData.organisation.count
    case 4:
        return tableData.central.count
    default:
        return 0
    }
}

override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    switch section {
    case 0:
        return "School"
    case 1:
        return "College"
    case 2:
        return "Office"
    case 3:
        return "Organisation"
    case 4:
        return "Central"
    default:
        return nil
    }
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath)

    // Get item
    var item: Result?
    switch section {
    case 0:
        item = tableData?.school[indexPath.row]
    case 1:
        item = tableData?.college[indexPath.row]
    case 2:
        item = tableData?.office[indexPath.row]
    case 3:
        item = tableData?.organisation[indexPath.row]
    case 4:
        item = tableData?.central[indexPath.row]
    default:
        break
    }

    if let item = item {
        // Configure the cell...
    }

    return cell
}

To fetch your data, you need to use a URLSession like that:

func fetchData() {
    guard let url = URL(string: "https://api.myjson.com/bins/r763z") else { return }

    let task = URLSession.shared.dataTask(with: url) { data, response, error in
        if let error = error {
            print("An error occurred: \(error)")
        } else if let data = data, let response = response as? HTTPURLResponse, response.statusCode == 200 {
            let decoder = JSONDecoder()
            do {
                let json = try decoder.decode(Root.self, from: data)
                tableData = json.data
                // Reload table view
                DispatchQueue.main.async {
                    self.tableView.reloadData()
                }
            } catch {
                print("Decoding error: \(error)")
            }
        }
    }
    task.resume()
}
Florentin
  • 1,433
  • 2
  • 13
  • 22
-1

To display the JSON in sections efficiently you have to decode the JSON into a struct with a title member

struct Root : Decodable {
    let status : Bool
    let sections : [Section]

    private enum CodingKeys : String, CodingKey { case status, data }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        status = try container.decode(Bool.self, forKey: .status)
        let data = try container.decode([String:[Result]].self, forKey: .data)
        sections = data.compactMap{ return $0.value.isEmpty ? nil : Section(title: $0.key, result: $0.value) }
    }
}

struct Section {
    let title : String
    let result : [Result]
}

struct Result : Decodable {
    let id, name, date : String
    let group : [String]
}

Declare a data source array

var sections = [Section]()

Assign the result to the array

do {
    let decoder = try JSONDecoder().decode(Root.self,  from: data!)
    let status = decoder.status

    if status == true {
        sections = decoder.sections
        DispatchQueue.main.async {
            self.tableView.reloadData()
        }
    } else {

    }
} catch { print(error) }

The relevant table view data source methods are

override func numberOfSections(in tableView: UITableView) -> Int {
    return sections.count
}

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

override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    return sections[section].title
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath)
    let item = sections[indexPath.section].result[indexPath.row]
    // Update the UI
}

Side note: Name your structs with more meaningful names. For example an array is supposed to be named with something in plural form (like in my previous suggestion)

vadian
  • 274,689
  • 30
  • 353
  • 361
  • Wow. how your thinking this much. great! I will check and update you here. Thank you so much – Soniya Aug 15 '19 at 08:07
  • If I click the tableview cell its possible to get cell all values, because I am going to pass those values to another viewcontroller. If possible update it – Soniya Aug 15 '19 at 08:16
  • That's another question. But anyway get the values from the **model** and not from the **view** – vadian Aug 15 '19 at 08:18
  • no no, Just I am asking didSelectRowAt to get all data. Another one doubt relevant to above question if suppose my array empty I don't want to show that section. Is it possible please give a hint I am struggling from two days. or else need to show one empty cell with empty data placeholder under the empty section. – Soniya Aug 15 '19 at 08:21
  • Once again this is beyond the scope of this question. – vadian Aug 15 '19 at 08:23
  • Yeah I understand but its just relevant to same track. if I want to ask that question separately need to post from the scratch. So, helpers may have a chance to do duplicate it. Thats my fear. Btw you gave me a fantastic solution. Thank you so much for your help. :) – Soniya Aug 15 '19 at 08:28
  • 1
    What is the reason to downvote my answer? This is a very efficient working solution. – vadian Aug 15 '19 at 08:29
  • I didn't gave downvote. I marked your answer. You saved my time how I do that. – Soniya Aug 15 '19 at 08:30
  • Its showing sorting result. How to show without sorting, I mean which is in JSON result same order need to show in tableview? – Soniya Aug 15 '19 at 09:33
  • The value for key `data` is a dictionary. Dictionaries are unordered by definition. If you need a specific order sort the array. – vadian Aug 15 '19 at 09:35
  • I don't want to do sorting. Its already showing sorting order in my tableview result. I want to remove that and want to show unordered, that mean which is I am receiving JSON same order need show? how to do that? – Soniya Aug 15 '19 at 09:36
  • The *same order* doesn’t mean *unordered*. It’s an arbitrary order. As I said a dictionary is **really** unordered. You have to maintain the order in the `init` method while mapping the dictionary to the array, – vadian Aug 15 '19 at 09:42
  • last only one doubt. my JSON array School, College, Office, Organisation, Central but in tableview output getting College, Office, School, Organisation. Can i do same order in tableview like which is in JSON. because my requirement like that only. don't mistake me for keep on asking. I said its new for me and trying to learn. – Soniya Aug 15 '19 at 17:49
  • I tried this link but its not working https://stackoverflow.com/questions/30969688/ordered-map-in-swift could you please tell me this last solution? – Soniya Aug 15 '19 at 18:33
  • is it possible to hide or remove section title if the section array empty? – Soniya Aug 18 '19 at 15:19
  • Yes, in `init(from decoder` `compactMap` `data` and return `nil` if `value` is empty. This omits the sections with empty arrays. – vadian Aug 18 '19 at 15:23
  • If you don't mind update your answer bit because I cant able to understand @vadian – Soniya Aug 18 '19 at 15:26
  • It's pretty easy. I updated the answer. Please try to read the documentation (at least the [Language Guide](https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html)) and learn something rather than asking for complete solutions – vadian Aug 18 '19 at 15:40