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"
}
}