Allissa Hertz
Allissa Hertz

Reputation: 33

Parse JSON Using Decodable into TableView Sections with an Array inside an Array

I am attempting to parse data from an api into a tableView with Sections. The end result would be a section title that corresponds with a month and rows with lists of videos that were posted for the month. The videos may not include a poster or description.

I tried to implement a for in loop function to update the model after retrieving the data, which worked great until I started trying to implement sections. I can print the json response to the console and receive the full response.

Here is a sample of the original JSON Structure:

{
    "page": {
        "type": "videos",
        "sections": [{
            "title": "September",
            "videos": [{
                "title": "Some Video",
                "description": "Video Description",
                "poster": "",
                "url": "url"
            }]
        }, {
            "title": "August 2019",
            "videos": [{
                "title": "Some Video",
                "description": "",
                "poster": "Some Image",
                "url": "url"
            }, {
                "title": "Some Video",
                "description": "No Description",
                "poster"",
                "url": "url"
            }]
        }]
    }
}

Here is my Model:

struct Root: Decodable {
    let page: Page  
}

struct Page: Decodable {
    let type: String
    let sections: [VideoSection]
}

struct VideoSection: Decodable {
    let title: String
    let videos: [Video]
}

struct Video: Decodable {
    let videoTitle: String
    let videoDescription: String?
    let poster: String?
    let url: String

    enum CodingKeys: String, CodingKey {
        case videoTitle = "title"
        case videoDescription = "description"
        case poster = "poster"
        case url = "url"
    }
}

Here is may Networking call with Parsing:

func getVideoData(url: String){

    guard let videoUrl = URL(string: "Video_URL") else { return }
    URLSession.shared.dataTask(with: videoUrl) { (data, response, error) in
        guard let data = data else { return }
        do {
            let decoder = JSONDecoder()
            let responseData = try decoder.decode(Root.self, from: data)

            DispatchQueue.main.async {    
                self.videoTableView.reloadData()
            }

        } catch let err {
            print("Error", err)
        }
    }.resume()
}

Here is my tableView:

var allvideosArray = [Video]()
var allSectionsArray = [VideoSection]()
var rootArray: Root?

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

    let cell = tableView.dequeueReusableCell(withIdentifier: "customVideoCell", for: indexPath) as! CustomVideoCell

    cell.videoDescriptionPlaceholder.text = Video.CodingKeys.videoDescription.rawValue
    cell.videoTitlePlaceholder.text = Video.CodingKeys.videoTitle.rawValue
    return cell
}

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

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

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return allvideosArray.count
}

When I attempt to print(responseData.page.sections.videos) I receive the error "Value of type'[VideoSection]' has no member 'videos,' which leads me to believe the issue has to do with the [videos] array inside of the [sections] array.

Upvotes: 1

Views: 158

Answers (1)

Shehata Gamal
Shehata Gamal

Reputation: 100533

You can try

var page:Page?

let responseData = try decoder.decode(Root.self, from: data)
self.page = responseData.page 
DispatchQueue.main.async { 
    self.videoTableView.reloadData()
} 

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 
    let cell = tableView.dequeueReusableCell(withIdentifier: "customVideoCell", for: indexPath) as! CustomVideoCell
    let item = page!.sections[indexPath.section].videos[indexPath.row]
    cell.videoDescriptionPlaceholder.text = item.videoDescription 
    cell.videoTitlePlaceholder.text = item.videoTitle
    return cell
}

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

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


func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return page?.sections[section].videos.count ?? 0

}

Upvotes: 1

Related Questions