Home > database >  How to set dat to the View from 2D Array type Api in SwiftUI?
How to set dat to the View from 2D Array type Api in SwiftUI?

Time:11-11

I have 2D type Array data in Json File And wants to fetch the data in View Model And I tried many Time and unable to fetch the data I used the Model which generated from the QuickType.io And I use the MVVM pattern for fetching the data.

This is My Json Data.

{ "publisherPlans": [[ "Name", "Free","Prime" ],["Book Sell",9999,9999],["Book Bulk Sell",0,9999],["Magazine start-up",9999,9999],["Demo book request count for School",5,9999],["Demo book request Acception",9999,9999],["Assign book for demo",25,9999]]}

And this is My Model which generated By website

public struct SchoolPlanModel: Decodable {
    public let publisherPlans: [[PublisherPlan]]

}

public enum PublisherPlan:Decodable {
    case integer(Int)
    case string(String)
}

And this is My ViewModel here I am trying to fetch data from Json file

class ReadData: ObservableObject {
    @Published var datas = [String]()
    
    func getData() async {
        guard let url = URL(string: "https://www.alibrary.in/api/plans") else { return }
        do {
            let (data, _) = try await URLSession.shared.data(from: url)
            Task{@MainActor in
                let results = try JSONDecoder().decode(SchoolPlanModel.self, from: data).publisherPlans
  print(results)
            }
        } catch {
            print("---> error: \(error)")
        }
    }
}

This My View Here I want to show Data.

struct SchoolPlanView: View{
    @StateObject var list = ReadData()
    
    var body: some View{
        ForEach(list.datas,id: \.self) { array in
                HStack{
                    ForEach(array.utf8CString, id: \.self) { element in
                        Text("\(element)")
                  }
                }
              }
        
    }
}

CodePudding user response:

try this approach, works for me. I had to modify a few things in your code, but should be straightforward to understand. Let me know if you need more explanations.

struct ContentView: View {
    var body: some View {
        SchoolPlanView()
    }
}

struct SchoolPlanModel: Decodable {
    var publisherPlans: [[PublisherPlan]]
}

enum PublisherPlan: Decodable, Hashable {
    case integer(Int)
    case string(String)

    // -- here 
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let x = try? container.decode(Int.self) {
            self = .integer(x)
            return
        }
        if let x = try? container.decode(String.self) {
            self = .string(x)
            return
        }
        throw DecodingError.typeMismatch(PublisherPlan.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for Plan"))
    }
}

class ReadData: ObservableObject {
    @Published var datas: [[PublisherPlan]] = []  // <-- here
    
    func getData() async {
        guard let url = URL(string: "https://www.alibrary.in/api/plans") else { return }
        do {
            let (data, _) = try await URLSession.shared.data(from: url)
            Task{@MainActor in
                let results = try JSONDecoder().decode(SchoolPlanModel.self, from: data)
                self.datas = results.publisherPlans // <-- here
            }
        } catch {
            print("---> error: \(error)")
        }
    }
}

struct SchoolPlanView: View{
    @StateObject var list = ReadData()
    
    var body: some View {
        // -- here
        List(list.datas, id: \.self) { array in
            HStack{
                ForEach(array, id: \.self) { item in
                    switch item {
                       case .integer(let int): Text("\(int)")
                       case .string(let str): Text(str)
                    }
                }
            }
        }
        .task {
            await list.getData()  // <-- here
        }
    }
}

EDIT-1:

To get the headings for columns, try this approach, where you separate the data and headings at the source, in getData(). You will have to adjust the display using columns etc...

class ReadData: ObservableObject {
    @Published var datas: [[PublisherPlan]] = []
    @Published var headings: [String] = []  // <-- here
    
    func getData() async {
        guard let url = URL(string: "https://www.alibrary.in/api/plans") else { return }
        do {
            let (data, _) = try await URLSession.shared.data(from: url)
            //       print("\n \(String(data: data, encoding: .utf8) as AnyObject) \n")
            Task{@MainActor in
                let results = try JSONDecoder().decode(SchoolPlanModel.self, from: data)
                // the data minus the first array of headings
                datas = Array(results.publisherPlans.dropFirst()) // <-- here
                // get the headings
                if let headers = results.publisherPlans.first { // <-- here
                    for h in headers {
                        switch h {
                           case .integer(_): break
                           case .string(let str): self.headings.append(str)
                        }
                    }
                }
            }
        } catch {
            print("---> error: \(error)")
        }
    }
}

struct SchoolPlanView: View{
    @StateObject var list = ReadData()
    
    var body: some View {
        VStack {
            HStack {
                ForEach(list.headings, id: \.self) { heading in  // <-- here
                    Text(heading)
                }
            }
            List(list.datas, id: \.self) { array in
                HStack {
                    ForEach(array, id: \.self) { item in
                        switch item {
                        case .integer(let int): Text("\(int)")
                        case .string(let str): Text(str)
                        }
                    }
                }
            }
        }
        .task {
            await list.getData()
        }
    }
}
  • Related