Home > Software engineering >  Question related to parsing JSON Data based on ID number in SwiftUI
Question related to parsing JSON Data based on ID number in SwiftUI

Time:01-09

I am new to SwiftUI and have encountered problems parsing data from JSON files in SwiftUI. At now, I can pass all the data in the JSON file but what I want is to parse a list of data based on their id number.

Here is my json file:

{
  "client": {
    "id": 200,
    "first_name": "Luke",
    "last_name": "",
    "email": "[email protected]"
  },
  "business_segments": [
    {
      "id": 1,
      "name": "Segment A",
      "status": "Open",
      "companies": [
        5,
        6,
        7
      ]
    },
    {
      "id": 2,
      "name": "Segment B",
      "status": "Open",
      "companies": [
        1,
        2,
        4
      ]
    },
    {
      "id": 3,
      "name": "Segment C",
      "status": "Open",
      "companies": [
        3,
        8,
        12
      ]
    },
    {
      "id": 4,
      "name": "Segment D",
      "status": "Open",
      "companies": [
        9,
        10,
        15
      ]
    },
    {
      "id": 5,
      "name": "Segment E",
      "status": "Open",
      "companies": [
        11,
        13,
        14,
        16
      ]
    },
    {
      "id": 6,
      "name": "Segment F",
      "status": "Open",
      "companies": [
        17,
        18
      ]
    }
  ],
  "companies": [
    {
      "id": 1,
      "name": "COMPANY A",
      "status": "Open"
    },
    {
      "id": 2,
      "name": "COMPANY B",
      "status": "Open"
    },
    {
      "id": 3,
      "name": "COMPANY C",
      "status": "Open"
    },
    {
      "id": 4,
      "name": "COMPANY D",
      "status": "Open"
    },
    {
      "id": 5,
      "name": "COMPANY E",
      "status": "Open"
    },
    {
      "id": 6,
      "name": "COMPANY F",
      "status": "Open"
    },
    {
      "id": 7,
      "name": "COMPANY G",
      "status": "Open"
    },
    {
      "id": 8,
      "name": "COMPANY H",
      "status": "Open"
    },
    {
      "id": 9,
      "name": "COMPANY I",
      "status": "Open"
    },
    {
      "id": 10,
      "name": "COMPANY J",
      "status": "Open"
    },
    {
      "id": 11,
      "name": "COMPANY K",
      "status": "Open"
    },
    {
      "id": 12,
      "name": "COMPANY L",
      "status": "Open"
    },
    {
      "id": 13,
      "name": "COMPANY M",
      "status": "Open"
    },
    {
      "id": 14,
      "name": "COMPANY N",
      "status": "Open"
    },
    {
      "id": 15,
      "name": "COMPANY O",
      "status": "Open"
    },
    {
      "id": 16,
      "name": "COMPANY P",
      "status": "Open"
    },
    {
      "id": 17,
      "name": "COMPANY Q",
      "status": "Open"
    },
    {
      "id": 18,
      "name": "COMPANY R",
      "status": "Open"
    }
  ]
}

As you can see from the JSON file, in my business segments section, I listed down a list of numbers. These numbers are the id number associated with the specific company in the company section. What I wish to do is have six buttons in the contentView. Each button represents one business segment. As I click on each button, it will jump to CompanyName View, where all the companies related to each business segment are shown. However, what I could do and what I have tired is to list all the companies in the CompanyName View. Hence, I need some help. I will show you different files in my project.

CompanyViewModel.swift, where handle all the parsing from json:

import Foundation


 // MARK: - CompanyData
 struct CompanyData: Codable,Hashable {
     let client: Client
     let businessSegments, companies: [BusinessSegment]

     enum CodingKeys: String, CodingKey {
         case client
         case businessSegments = "business_segments"
         case companies
     }
 }

 // MARK: - BusinessSegment
 struct BusinessSegment: Codable,Hashable {
     let id: Int
     let name: String
     let status: Status
     let companies: [Int]?
 }

 enum Status: String, Codable {
     case statusOpen = "Open"
 }

 // MARK: - Client
 struct Client: Codable,Hashable {
     let id: Int
     let firstName, lastName, email: String

     enum CodingKeys: String, CodingKey {
         case id
         case firstName = "first_name"
         case lastName = "last_name"
         case email
     }
 }

 
 class CompanyViewModel:ObservableObject{
     
      @Published var company_data: [CompanyData] = []
     
     func getUserData(){
         
         guard let url = Bundle.main.url(forResource:"Company", withExtension:"json") else {
             print("Invalid URL")
             return
         }
         
         let task = URLSession.shared.dataTask(with:url){ data, response, error in
             let decoder = JSONDecoder()
             if let data = data {
                 do {
                     let company_data = try decoder.decode(CompanyData.self, from:data)
                     
                      DispatchQueue.main.async{
                          self.company_data = [company_data]
                      }
                    
                     print(company_data.businessSegments)
                     print()
                     print(company_data.companies)
                     print()
                     
                     
                 } catch {
                     print("cannot decode")
                     print(String(data:data, encoding:.utf8) as Any)
                     print(error)
                 }
             }
             
         }
         
         task.resume()
         
         
     }
 }

 
 

ContentView.swift

import SwiftUI

struct ContentView: View {
    @StateObject private var companyVM = CompanyViewModel()
    var body: some View {
        NavigationView{
            VStack {
                ForEach(companyVM.company_data,id:\.self){ item in
                    ForEach(item.businessSegments,id:\.self){ segment in
                        /*Text(segment.name)
                         .multilineTextAlignment(.center)
                         .bold()
                         .font(.custom("Montserrat-Bold", size: 24)) */
                        
                        
                        NavigationLink(destination:
                                        CompanyName(segment: segment.name)){
                            Text(segment.name)
                                .frame(width:240,height:50)
                                .bold()
                                .foregroundColor(.white)
                                .background(Color.orange)
                                .clipShape(Capsule())
                         }
                        
                        
                    }
                }
            }
            .padding()
            .onAppear(){
                companyVM.getUserData()
            }
        }
    }
}


struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

CompanyName.swift

import SwiftUI

struct CompanyName: View {
    let segment: String
    @StateObject private var companyVM = CompanyViewModel()
    var body: some View {
        NavigationView{
            VStack{
                Text(segment)
                    .multilineTextAlignment(.center)
                    .bold()
                    .font(.custom("Montserrat-Bold", size: 24))
                
                ForEach(companyVM.company_data,id:\.self){ item in
                    ForEach(item.companies,id:\.self){ company in
                        Text(company.name)
                            .multilineTextAlignment(.center)
                            .bold()
                            .font(.custom("Montserrat-Bold", size: 24))
                    }
                }
                
            }.padding()
                .onAppear(){
                    companyVM.getUserData()
                }
        }
    }
}
    struct CompanyName_Previews: PreviewProvider {
        static var previews: some View {
            CompanyName(segment: "Business Segments")
        }
    }


CodePudding user response:

Here is the code that displays the companies related to the selected segment. As you tap a segment, the companies for that particular segment are retrieved (based on id) from; a particular CompanyData or from all [CompanyData]. Adjust the code to select one or the other, both functions are presented in the CompanyViewModel.

Note, I left the @Published var company_data: [CompanyData], although you said you only have one CompanyData in it.

Have a look at this link, it gives you examples of how to use ObservableObject and manage data in your app https://developer.apple.com/documentation/swiftui/managing-model-data-in-your-app

class CompanyViewModel: ObservableObject {
    @Published var company_data: [CompanyData] = []
    
    func getUserData(){
        guard let url = Bundle.main.url(forResource:"Company", withExtension:"json") else {
            print("Invalid URL")
            return
        }
        let task = URLSession.shared.dataTask(with:url){ data, response, error in
            let decoder = JSONDecoder()
            if let data = data {
                do {
                    let company_data = try decoder.decode(CompanyData.self, from:data)
                    DispatchQueue.main.async{
                        self.company_data = [company_data]
                    }
                    print(company_data.businessSegments)
                    print()
                    print(company_data.companies)
                    print()
                } catch {
                    print("cannot decode")
                    print(String(data:data, encoding:.utf8) as Any)
                    print(error)
                }
            }
        }
        task.resume()
    }
    
    // get all companies for a particular segment and from a particular CompanyData
    func getCompaniesFor(seg: BusinessSegment, item: CompanyData) -> [BusinessSegment] {
        var companies = [BusinessSegment]()
        if let segCompanies = seg.companies {
            for id in segCompanies {
                for company in item.companies {
                    if company.id == id {
                        companies.append(company)
                    }
                }
            }
        }
        return companies
    }
    
    // get all companies for a particular segment from all [CompanyData]
    func getAllCompaniesFor(seg: BusinessSegment) -> [BusinessSegment] {
        var companies = [BusinessSegment]()
        if let segCompanies = seg.companies {
            company_data.forEach { company_data in
                for company in company_data.companies {
                    for id in segCompanies {
                        if company.id == id {
                            companies.append(company)
                        }
                    }
                }
            }
        }
        return companies
    }
    
}

struct ContentView: View {
    @StateObject var companyVM = CompanyViewModel() // <-- here
    
    var body: some View {
        NavigationView{
            VStack {
                ForEach(companyVM.company_data){ item in
                    ForEach(item.businessSegments){ segment in
                        /*Text(segment.name)
                         .multilineTextAlignment(.center)
                         .bold()
                         .font(.custom("Montserrat-Bold", size: 24)) */
                        
                        // --- here
                        NavigationLink(destination:
                                        CompanyName(segment: segment, companyVM: companyVM)){
                            Text(segment.name)
                                .frame(width:240,height:50)
                                .bold()
                                .foregroundColor(.white)
                                .background(Color.orange)
                                .clipShape(Capsule())
                        }
                    }
                }
            }
            .padding()
            .onAppear {
                companyVM.getUserData()
            }
        }
    }
}

struct CompanyName: View {
    let segment: BusinessSegment  // <-- here
    @ObservedObject var companyVM: CompanyViewModel // <-- here
    
    var body: some View {
        NavigationView{
            VStack{
                Text(segment.name).foregroundColor(.blue)
                    .multilineTextAlignment(.center)
                    .bold()
                    .font(.custom("Montserrat-Bold", size: 24))
                
                // --- here
                ForEach(companyVM.getAllCompaniesFor(seg: segment)){ company in
                    Text(company.name)
                        .multilineTextAlignment(.center)
                        .bold()
                        .font(.custom("Montserrat-Bold", size: 24))
                }
                
            }.padding()
        }
    }
    
}

// MARK: - CompanyData
struct CompanyData: Identifiable, Codable,Hashable { // <-- here
    let id = UUID() // <-- here
    let client: Client
    let businessSegments, companies: [BusinessSegment]
    
    enum CodingKeys: String, CodingKey {
        case client, companies
        case businessSegments = "business_segments"
    }
}

// MARK: - BusinessSegment
struct BusinessSegment: Identifiable, Codable,Hashable { // <-- here
    let id: Int
    let name: String
    let status: String
    let companies: [Int]?
}

// MARK: - Client
struct Client: Identifiable, Codable,Hashable { // <-- here
    let id: Int
    let firstName, lastName, email: String
    
    enum CodingKeys: String, CodingKey {
        case id, email
        case firstName = "first_name"
        case lastName = "last_name"
    }
}
  • Related