Learn Courses My Dashboard

Display only 'true' JSON property in NavigationView

As I complete the 90 day lesson I am building an app much like the Recipe App. I have a list of JSON data. I only want to display certain data if the ‘checklists’ == true. It seems to only be pulling the boolean from the last data set in the JSON file. If it is true it lists all the data in the list (even if that datas ‘checklists’ was false. If the last data set was false it lists none. Newbie here…appreciate all the help. I suspect it has something to do with the ,ForEach’.

struct ChecklistView: View {
    
    //reference the view model
    @ObservedObject var model = TitleModel()
    
    var body: some View {
        
        NavigationView {
            
            //loop through each title
            ForEach (0..<model.titles.count) { index in
                
                List(model.titles) { r in
                    
                    //only show those that are true
                    if model.titles[index].checklists == true {

                        
                                NavigationLink(
                                    
                                    destination: DetailView(detail:r),
                                    label: {
                                            HStack(spacing: 20.0) {
                                                Image(r.image1)
                                                    .resizable()
                                                    .scaledToFill()
                                                    .frame (width: 50, height: 50, alignment: .center)
                                                    .clipped()
                                                    .cornerRadius(5)
                                                Text(r.title)
                                            }
                                    })
                    }
                }
            }
        }
    }
}

struct ChecklistView_Previews: PreviewProvider {
    static var previews: some View {
        ChecklistView()
            .environmentObject(TitleModel())
    }
}`

@Ready7

Hi Shawn,

Welcome to the community.

Does your data model, that describes your json, have an id property?

Can you paste in your data model in a reply and also paste in your json code.

Thanks for the help here Chris.

JSON and Data Model to follow…

[
    {
        "category": "Medical",
        "title": "STAT-MD",
        "ID": "1.0",
        "checklists":true,
        "preflight":true,
        "taxi_takeoff":true,
        "en_route":true,
        "app_land":true,
        "postflight":true,
        "weather":false,
        "non_normal":true,
        "maintenance":false,
        "customer_service":false,
        "fleet": "All",
        "stage": "All",
        "image1": "STATMD1",
        "image2": "",
        "image3": "",
        "image4":"",
        "pub1": "FOM",
        "reference1": "17.4",
        "pub2": "",
        "reference2": "",
        "pub3": "",
        "reference3": "",
        "remark": [
            "Updated 9/23/2022."
        ]
    },
    {
        "category": "Turbulence",
        "title": "Turbulence",
        "ID": "1.0",
        "checklists":false,
        "preflight":true,
        "taxi_takeoff":true,
        "en_route":true,
        "app_land":true,
        "postflight":true,
        "weather":false,
        "non_normal":true,
        "maintenance":false,
        "customer_service":false,
        "fleet": "All",
        "stage": "All",
        "image1": "Turbulence1",
        "image2": "",
        "image3": "",
        "image4": "",
        "pub1": "FOM",
        "reference1": "",
        "pub2": "",
        "reference2": "",
        "pub3": "",
        "reference3": "",
        "remark": [
            "Updated 9/23/2022."
        ]
    }
]
import Foundation

class Title: Identifiable, Decodable {
    
    var id:UUID?
    var category:String
    var title:String
    var ID:String
    var checklists:Bool
    var preflight:Bool
    var taxi_takeoff:Bool
    var en_route:Bool
    var app_land:Bool
    var postflight:Bool
    var weather: Bool
    var non_normal:Bool
    var maintenance:Bool
    var customer_service:Bool
    var fleet:String
    var stage:String
    var image1:String
    var image2:String
    var image3:String
    var image4:String
    var pub1:String
    var reference1:String
    var pub2:String
    var reference2:String
    var pub3:String
    var reference3:String
    var remark:[String]
    
}

I am essentially trying to do the same as you did in the Recipe App on the ‘Featured List’ however rather than the swipe-able format I want to use the NavigationView. If ‘checklists’ = true then I want it displayed on the NavigationView list, false it will not be displayed. As you can see I have several boolean values in my JSON file…so for different “category/title” NavigationViews I will use this ForEach to only display certain categories of information (ie app_land may be false in one “category” but true in several others. There are some Lists where I may have several of the Keys = true to have a more inclusive list. Thanks again for pointing me in the right direction. Your course is very helpful and a nice organized approach to learning!

I also tried imbedding it into the NavigationView like this…

import SwiftUI

struct ContentView: View {
    
    //reference the view model
    @ObservedObject var model = TitleModel()
    
    var body: some View {
        
        NavigationView {
            
                List(model.titles) { r in
                    
                                NavigationLink(
                                    
                                    destination: DetailView(detail:r),
                                    label: {
                                        
                                        //loop through each title
                                        ForEach (0..<model.titles.count) { index in      //only show those that are true
                                            if model.titles[index].checklists == true {
                                                HStack(spacing: 20.0) {
                                                    Image(r.image1)
                                                        .resizable()
                                                        .scaledToFill()
                                                        .frame (width: 50, height: 50, alignment: .center)
                                                        .clipped()
                                                        .cornerRadius(5)
                                                    Text(r.title)
                                                }
                                            }
                                        }
                                    })
                }
                
            
        }
    }
}

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

@Ready7

Hi Shawn,

In your decoding function, are you populating the id field with a UUID() after decoding the json?

Good morning Chris. Looks like I am in the DataService func below in the “do/catch”.

import Foundation

class DataService {
    
    static func getLocalData() -> [Title] {
    
        //parse local json file
        //get a url path to the json file
        let pathString = Bundle.main.path(forResource: "titles", ofType: "json")
        
        //check if pathString is not nil, otherwise declare an empty string
        guard pathString != nil else {
            return[Title]()
        }
        
        //create url object
        let url = URL(fileURLWithPath: pathString!)
            
        do {
            //create a data object
            let data = try Data(contentsOf: url)
            
            //decode the data with a JSON decoder
            let decoder = JSONDecoder()
             
            do {
                
                let titleData = try decoder.decode([Title].self, from: data)
                
                //add the unique IDs
                for r in titleData {
                    r.id = UUID()
                }
                
                //return the categories
                return titleData
            }
            catch {
                //error with parsing json
                print(error)
            }
            
        }
        catch {
            //error with getting data
            print(error)
            
        }
        
        return [Title]()
    }
}

This is definitely one of those moments you mention…frustrating but I have no doubt probably learned more running down these rabbit holes trying to find the issue. Thanks again for your timely responses and appreciate any help.

Enjoy your day,
Shawn

Not sure what the issue might be.

Might be best to share your entire project with that current json file you have so that I can have a closer look.

In Finder, can you compress your project at the top level folder by selecting it and then right click and select “Compress YourProjectName”.

Upload that to either Dropbox or Google Drive and create a share link there and then post that share link in a reply.

Thanks for taking a look at this. Very much appreciated.

@Ready7

Hi Shawn,

You need to compress the project at the top level in which the ProjectName.xcodeproj file and the subfolder ProjectName which contains all of the other files and folders of the project reside. That will encompass the entire project. See the attached image.

Good morning. Sorry about that.

Hi Shawn,

I downloaded your project but I noticed that the files were all in the wrong place in Finder and my version of Xcode was complaining so I rebuilt the project by creating a new version and putting the files in the right place.

Did you move files around in the Project Navigator panel (the one on the left) or in Finder?

Anyway, I have got it working after changing the way you had the ChecklistView laid out.

Here is a link to the updated version:

By the way, I have Xcode 14 so if you are using an earlier version you wont be able to open this one so in that case here is the change to CheckListView.

struct ChecklistView: View {
    
    //reference the view model
    @EnvironmentObject var model: TitleModel
    
    var body: some View {
        
        NavigationView {
            List(model.titles) { title in
                //only show those that are true
                if title.checklists == true {
                    NavigationLink(

                        destination: DetailView(detail: title),
                        label: {
                            HStack(spacing: 20.0) {
                                Image(systemName: "checklist")
                                    .resizable()
                                    .scaledToFit()
                                    .frame (width: 50, height: 50, alignment: .center)
                                    .clipped()
                                    .cornerRadius(5)
                                Text(title.title)
                            }
                        })
                }
            }

        }
    }
}

Good morning Chris. I can’t thank you enough for helping me out there. I noticed the changes.

-you dropped the ForEach and just used an if
-changed the @ObservedObject to @EnvironmentObject which changed the data flow you alluded to

Enjoy the weekend and thanks for helping me get through this barrier!
Shawn

1 Like