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()
}
}
}
