Home > OS >  How to load data from more then one local JSON file to the labels in table view using swift
How to load data from more then one local JSON file to the labels in table view using swift

Time:09-17

I have two folders, each of them contains image, name of song and JSON file: enter image description here Need to figure out, how to load data from each of JSON. Maybe it should be an array. JSON Files:

Second folder:

{
    "beatpacktwo":
    {
        "twoloops": [
            {
                "name": "Midnight",
                "producer": "Stefan Guy",
                "count": "32",
                "genre": "R&B",
                "imagename": "beatpacktwo"
            }
        ]
    }
}

First folder:

{
    "beatpackone":
    {
        "oneloops": [
            {
                "name": "Away we go",
                "producer": "Tubular Kingz",
                "count": "28",
                "genre": "Lo-fi Hip Hop",
                "imagename": "beatpackone"
            }
        ]
    }
}

Here is the swift file for each of folder: BeatPackTwo

import Foundation
import UIKit

struct BeatPackTwo: Decodable {
    let beatpacktwo: BeatPackDataTwo
}

struct BeatPackDataTwo: Decodable {
    let oneloops: [LoopTwo]
}

struct LoopTwo: Decodable {
    let name: String
    let producer: String
    let count: String
    let genre: String
    let imagename: String
}

public class DataLoaderTwo {
    
    func parseJSONtwo() -> BeatPackDataTwo? {
        guard let url = Bundle.main.url(forResource: "beatpacktwo", withExtension: "json") else {
            print("\n-------> bundle path error")
            return nil
        }
        
        do {
            let jsonData = try Data(contentsOf: url)
            let response = try JSONDecoder().decode(BeatPackTwo.self, from: jsonData)
            print("\n-------> response: \(response)")
            return response.beatpacktwo
        }
        catch {
            print("\n====> error: \(error)" )
            return nil
        }
    }
}

BeatPackOne:

import Foundation
import UIKit

struct BeatPackOne: Decodable {
    let beatpackone: BeatPackDataOne
}

struct BeatPackDataOne: Decodable {
    let oneloops: [LoopOne]
}

struct LoopOne: Decodable {
    let name: String
    let producer: String
    let count: String
    let genre: String
    let imagename: String
}

public class DataLoaderOne {
    
    func parseJSONone() -> BeatPackDataOne? {
        guard let url = Bundle.main.url(forResource: "beatpackone", withExtension: "json") else {
            print("\n-------> bundle path error")
            return nil
        }
        
        do {
            let jsonData = try Data(contentsOf: url)
            let response = try JSONDecoder().decode(BeatPackOne.self, from: jsonData)
            print("\n-------> response: \(response)")
            return response.beatpackone
        }
        catch {
            print("\n====> error: \(error)" )
            return nil
        }
    }
}

I've tried to create an instance in view controller, but now I can load the data from the one JSON file only, need to load from each of them and numberofrows in section should return count of the folders. Here is my tableview in view controller:

extension BPLibraryViewController: UITableViewDataSource, UITableViewDelegate, UIScrollViewDelegate {
    
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 180
    }
    
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return loopsName.count
    }
    
    public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        let cell = tableView.dequeueReusableCell(withIdentifier: S.newOneCell, for: indexPath) as! BeatPackLibraryCell
        
        let loopsCount = loopsName[indexPath.row]  //Instance of our arrays from JSON
        
        cell.loopNameLabel.text = loopsCount.name
        cell.producerNameLabel.text = loopsCount.producer
        cell.loopsCountLabel.text = String(beatLoops.count)
        cell.genreLabel.text = loopsCount.genre
        cell.firstBeatButtonLabel.setImage(UIImage(named: loopsCount.imagename), for: .normal)
        
        cell.delegate = self
        cell.selectionStyle = .none
        cell.tag = indexPath.row
        
        if let playingCell = currentPlayingIndex, playingCell == indexPath.row {
            cell.firstBtnOutlet.setImage(UIImage(named: "pauseButtonPack.png"), for:
                                            .normal)
        } else {
            cell.firstBtnOutlet.setImage(UIImage(named: "playButtonPack.png"), for:
                                            .normal)
        }
        return cell
    }

It should be something like that: enter image description here

CodePudding user response:

There is no reason to have two JSON with different structures in your case:

Let's have instead:

{
    "beatPack":
    {
        "loops": [
            {
                "name": "Midnight",
                "producer": "Stefan Guy",
                "count": "32",
                "genre": "R&B",
                "imagename": "beatpacktwo"
            }
        ]
    }
}

and

{
    "beatPack":
    {
        "loops": [
            {
                "name": "Away we go",
                "producer": "Tubular Kingz",
                "count": "28",
                "genre": "Lo-fi Hip Hop",
                "imagename": "beatpackone"
            }
        ]
    }
}

I kept beatPack & loops, but that might even be irrelevant to keep them.

Then we have for our model:

struct TopLevel: Decodable {
    let beatPack: BeatPack
}

struct BeatPack: Decodable {
    let loops: [Loop]
}

struct Loop: Decodable {
    let name: String
    let producer: String
    let count: String
    let genre: String
    let imagename: String
}

Then, to parse them:

In your BPLibraryViewController:

var loopsName = [Loop]()

func parseJSON(file fileName: String) -> BeatPack? {
    guard let url = Bundle.main.url(forResource: fileName, withExtension: "json") else {
        print("\n-------> bundle path error: File \(fileName) not found")
        return nil
    }
    
    do {
        let jsonData = try Data(contentsOf: url)
        let response = try JSONDecoder().decode(TopLevel.self, from: jsonData)
        print("\n-------> response: \(response)")
        return response.beatPack
    }
    catch {
        print("\n====> error: \(error)" )
        return nil
    }
}



func retrieveLoops() -> [Loop] {
    let packOne = parseJSON(file: "beatpackone")
    let packTwo = parseJSON(file: "beatpacktwo")
    var loops = [Loop]()
    loops.append(contentsOf: packOne?.loops ?? [])
    loops.append(contentsOf: packTwo?.loops ?? [])
    // or, if you prefer more explicitly:
    // if let loopOne = packOne?.loops {
    //     loops.append(contentsOf: loopOne)
    // }
    return loops
}

func prepareInit() {
    loopsNames = retrieveLoops()
    tableView.reloadData()
}
  • Related