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