Creating an Array/Button with specific ID everybutiton regardless of projectName or Hours

hi all, I'm writing to DevForum after few days of struggle 13-15h everyday trying to dig many forum for solution ( try Github for: do to list and many other app built in to switch between toggle and button to see if the issues was coming from here

My App idea seems simple actually very complex to build for me, I have manage to build the solution for one row and it's working but when it goes to adding the row into a list I'm giving up.... I do not understand why in my TimeKeyInRow structure it's working I can clic and get feedback of every single hours but when I add row to a list even if I have already created an ID it's still taking the all row as one isSet


import SwiftUI

struct HourButton: View,Identifiable {
    var id: String = "hour"
    @State var isSet: Bool
    var value: Double = 1
    
    var body: some View {
        HStack {
            Button {
                isSet.toggle()
                print("clic already \(isSet) \(id)h & \(value) has Value")
                
            } label: {
                Label("8h", systemImage: isSet ? "circle.fill" : "circle")
                    .labelStyle(.iconOnly)
                    .foregroundStyle(isSet ? .blue : .gray)
                
            }.id(id)
            
        }
        
        }
}

struct HourButton_Previews: PreviewProvider {
    static var previews: some View {
        //#Preview {
        HourButton(id: "MORN", isSet: true, value: 3)
        
    }
}

This is the Row Part which is working fine

import SwiftUI

struct TimeKeyInRow: View,Identifiable {
    var id: String
    var project: Project
    @Binding var isSet: Bool
    let listOfPossibleHours: [Double] = [0.5,1,2,3,4,8]
    var body: some View {
        HStack {
                   
                      
                    Text(project.shortname)


                    Spacer()
            
                  
            HourButton(id: "\(project.shortname)\(0)", isSet: true, value: listOfPossibleHours[0])
            HourButton(id: "\(project.shortname)\(1)", isSet: true, value: listOfPossibleHours[1])
            HourButton(id: "\(project.shortname)\(2)", isSet: true, value: listOfPossibleHours[2])
            HourButton(id: "\(project.shortname)\(3)", isSet: true, value: listOfPossibleHours[3])
            HourButton(id: "\(project.shortname)\(4)", isSet: true, value: listOfPossibleHours[4])

            
            Text("\(project.leftPMtime)")
                       }
                }
    }

struct TimeKeyInRow_Previews: PreviewProvider {
    static var previews: some View {
        //#Preview {
        let projects = ModelData().projects
        
        TimeKeyInRow(id: "test", project: projects[2], isSet: .constant(true))
    }
}

Then the part were the issues is coming


import SwiftUI

struct TimeKeyinList: View, Identifiable{
    var id: String
    
    @Environment(ModelData.self) var modelData
    let projects = ModelData().projects
    @State var isSet: Bool
    @State private var hideTeco = false
    
    
    var filteredProjects: [Project] {
        // removeTECO project
        modelData.projects.filter { project in
            !project.isTeco
        }}
    var body: some View {
        VStack {
            HStack {
                Text("TIME KEY_IN")
                    .font(.title)
                    .multilineTextAlignment(.leading)
                    .bold()
                Spacer()
                Text("8h")
                    .font(.title)
                    .multilineTextAlignment(.leading)
                    .bold()
                
            }.padding()
            
            
            
            
            List {
                HStack(alignment: .top) {
                    Text("Project Name")
                        .frame(width: 80, height: 25)
                        .font(.caption)
                        .multilineTextAlignment(.center)
                        .lineLimit(8)
                        .allowsTightening(/*@START_MENU_TOKEN@*/true/*@END_MENU_TOKEN@*/)
                        .multilineTextAlignment(.center)
                        .bold()
                    Spacer(minLength:30)
                    
                    Text("0.5h")
                        .font(.footnote)
                    
                    
                    
                    
                    Text("1h")
                        .font(.footnote)
                        .padding(/*@START_MENU_TOKEN@*/EdgeInsets()/*@END_MENU_TOKEN@*/)
                    
                    Text("2h")
                        .font(.footnote)
                        .padding(/*@START_MENU_TOKEN@*/EdgeInsets()/*@END_MENU_TOKEN@*/)
                    Text("3h")
                        .font(.footnote)
                        .padding(/*@START_MENU_TOKEN@*/EdgeInsets()/*@END_MENU_TOKEN@*/)
                    Text("4h")
                        .font(.footnote)
                        .padding(/*@START_MENU_TOKEN@*/EdgeInsets()/*@END_MENU_TOKEN@*/)
                    
                    
                    
                    
                    
                    
                    Text("Time Left")
                        .frame(width: 60, height: 25)
                        .font(.caption)
                        .multilineTextAlignment(.center)
                        .lineLimit(8)
                        .allowsTightening(/*@START_MENU_TOKEN@*/true/*@END_MENU_TOKEN@*/)
                        .multilineTextAlignment(.center)
                        .bold()
                    
                }
                ForEach (filteredProjects)  { project in
                    
                    TimeKeyInRow(id: project.shortname, project: project, isSet: $isSet)
                    
                    
                
                    
                }
                                Divider()
                
//                TimeKeyInRow(project: "TRAINING", isSet: $isSet)
                //                TimeKeyInRow(isSet: $isSet, projectshortname: "MEETING", timeleft: 0)
                //                TimeKeyInRow(isSet: $isSet, projectshortname: "NON ALLOCATED", timeleft:0)
                //                TimeKeyInRow(isSet: $isSet, projectshortname: "HOLIDAY", timeleft:0)
                //
                
                //            }
                //            .listStyle(.inset)
                //            DatePicker(selection: /*@START_MENU_TOKEN@*/.constant(Date())/*@END_MENU_TOKEN@*/, label: { /*@START_MENU_TOKEN@*/Text("Date")/*@END_MENU_TOKEN@*/ })
            }.labelsHidden()
        }
    }
}
struct TimeKeyinList_Previews: PreviewProvider {
    static var previews: some View {
        //#Preview {
        TimeKeyinList(id: "test", isSet: true).environment(ModelData())
    }
}



Answered by Claude31 in 779775022

You do not show how you initialise

    @State var isSet: [Bool]

Why do you repeat projects initialisation in TimeKeyInRow and in TimeKeyinList ? If I understand your intent, you should only pass a single project.

So, I have added an initialisation in .onAppear. This is just a quick test. Of course, you have to update isBool each time a isTeco toggles or each time you add a new Project.

That's just a quick fix, to show the principle.

extension String {  // To align text
    func paddedToWidth(_ width: Int) -> String {
        let length = self.count
        guard length < width else {
            return self
        }

        let spaces = Array<Character>.init(repeating: " ", count: width - length)
        return self + spaces
    }
}

struct TimeKeyInRow: View,Identifiable {
    var id: String
    var project: Project. // Just pass the project
    /* Do not redeclare
    var projects = [
        Project(name: "Project1, isTeco = false, shortname = MORN, leftPMTime = 131",shortname : "MORN", leftPMtime : 131, isTeco: false),
        Project(name: "Project2, isTeco = false, shortname = MORN, leftPMTime = 122",shortname : "IFF", leftPMtime : 122, isTeco: true),
        Project(name: "Project3, isTeco = false, shortname = MORN, leftPMTime = 133",shortname : "FFI", leftPMtime : 133, isTeco: true),
        Project(name: "Project4, isTeco = false, shortname = MORN, leftPMTime = 444",shortname : "IFFCO", leftPMtime : 444, isTeco: false)
    ] */
    
    @Binding var isSet: Bool
    let listOfPossibleHours: [Double] = [0.5,1,2,3,4,8]
    
    var body: some View {
        HStack {
            Spacer()    // ADDED
            Text(project.shortname.paddedToWidth(5)) // Text(projects[1].shortname)
                .font(.custom("Menlo", size: 16)) // <<-- To get proper alignment
            Spacer()
            ForEach(listOfPossibleHours, id: \.self) { hour in
                HourButton(id: "\(project.shortname)", isSet: isSet, value: hour)
            }
            /* replace this
            HourButton(id: "\(project.shortname)\(0)", isSet: isSet, value: listOfPossibleHours[0])
            HourButton(id: "\(project.shortname)\(1)", isSet: isSet, value: listOfPossibleHours[1])
             // What do you expect with  \(0)
             */
            Text("\(project.leftPMtime)")
                .font(.custom("Menlo", size: 16)) // <<-- To get proper alignment
            Spacer()    // <<-- ADDED
        }
    }
}


struct HourButton: View,Identifiable {
    
    var id: String = "hour"
    @State var isSet: Bool
    var value: Double = 1
    
    var body: some View {
        HStack {
            Button {
                isSet.toggle()
                print("clic already \(isSet) \(id)h & \(value) has Value")
                
            } label: {
                Label("8h", systemImage: isSet ? "circle.fill" : "circle")
                    .labelStyle(.iconOnly)
                    .foregroundStyle(isSet ? .blue : .gray)
                
            }.id(id)
            
        }
        
    }
}

struct TimeKeyinList: View, Identifiable { 
    var id: String = "" // to ease test
    var projects = [
        Project(name: "Project1, isTeco = false, shortname = MORN, leftPMTime = 131",shortname : "MORN", leftPMtime : 131, isTeco: false),
        Project(name: "Project2, isTeco = false, shortname = MORN, leftPMTime = 122",shortname : "IFF", leftPMtime : 122, isTeco: true),
        Project(name: "Project3, isTeco = false, shortname = MORN, leftPMTime = 133",shortname : "FFI", leftPMtime : 133, isTeco: true),
        Project(name: "Project4, isTeco = false, shortname = MORN, leftPMTime = 444",shortname : "IFFCO", leftPMtime : 444, isTeco: false)
    ]
    @State var isSet: [Bool] = [true, false, false, true]
    @State private var hideTeco = false
    
    var filteredProjects: [Project] {
        // removeTECO project
        projects.filter { project in
            !project.isTeco
        }}
    
    var body: some View {
        VStack {
            ForEach(Array(filteredProjects.enumerated()), id: \.offset) { (row, project) in
                TimeKeyInRow(id: project.shortname, project: project, isSet: $isSet[row])
            }
        }
        .labelsHidden()
        .onAppear() {   // <<-- Added
            for (row, project) in filteredProjects.enumerated() {
                isSet[row] = !project.isTeco
            }
        }
    }
}

struct Project: Identifiable {
    var id: UUID = UUID()
    var name: String
    var shortname: String
    var leftPMtime: Int
    var isTeco: Bool

}

Note: What is isTeco meaning ?

In some cases, neither shortName not isTeco match with label:

Project(name: "Project2, isTeco = false, shortname = MORN, leftPMTime = 122",shortname : "IFF", leftPMtime : 122, isTeco: true),

Is it on purpose ?

It's a bit hard to track your code, but let's try.

In TimeKeyinList, you have a single var:

    @State var isSet: Bool

And in the loop, you use it for every TimeKeyInRow

ForEach (filteredProjects)  { project in
                    TimeKeyInRow(id: project.shortname, project: project, isSet: $isSet)

So, logically, when you change for one, you change for all.

The state var should be an array of Bool indexed by row or a dictionary, the key being the project.id and the value isSet.

Something like (I let you test):

struct TimeKeyinList: View, Identifiable {

    @State var isSet: [Bool]

and the loop:

                ForEach(Array(filteredProjects.enumerated()), id: \.offset) { (row, project) in
                    TimeKeyInRow(id: project.shortname, project: project, isSet: $isSet[row])

Or with dictionary:

    @State var isSet: [String:Bool]

and the loop:

ForEach (filteredProjects)  { project in
                    TimeKeyInRow(id: project.shortname, project: project, isSet: $isSet[project.id] ?? false)

If that works, don't forget to close the thread by marking the answer as correct. Otherwise, explain what's the problem.

Hi @Claude31, first of all thank you very much for trying to help, this is very appreciated, I have tried both solution but issues is still the same ( either the app crash without any warning or advice on where is the issuess...... only giving me crazy line of code...

when I click one of the row created by the for each project filtered all the buttons change from true to false ( regardless of the id... I really don;t get what I'm doing wrong)

I started to understand your logic when I read your prompt mentioned above but either I do it wrongly or either I beelieve is coming from my List creation....

anyway I did follow you advice and copy only the interesting part for you here is my code which fit only into one file

with his code you will be able to see what I'm struggling with.

what I need is to have a ROW which have 5 button each button represent a timing , 0.5h 1h,2h,3,4,8h , then user will be able to clic any of those button , the catch is that it's working in a rowtimekeyin as you ca see on the code below but when I create a list of row , like you set my asset seems to be look to the rowitelf and not each button as I need to. Because the end target it to let the user click at the end of the day the number of hours for each project he word on but if he key-in more than 5 hours all the remaining hours available (not click) which are more than 3h (because 8h per days max- 5hours already choose) those will not be clickable anymore with a warning saying that he already chose 5h therefore only 3h and lower will be clickable. but that will be a data logic file, for now I'm building the UI and as you can see I'm diving into forum.....


//
//  TimeKeyInRow.swift
//  MyPmV1
//
//  Created by Sebastien BENAVIDES on 9/2/24.
//
import Foundation
import SwiftUI

struct TimeKeyInRow: View,Identifiable {
    var id: String
    var projects = [
            Project(name: "Project1, isTeco = false, shortname = MORN, leftPMTime = 131",shortname : "MORN", leftPMtime : 131, isTeco: false),
            Project(name: "Project2, isTeco = false, shortname = MORN, leftPMTime = 122",shortname : "IFF", leftPMtime : 122, isTeco: true),
            Project(name: "Project3, isTeco = false, shortname = MORN, leftPMTime = 133",shortname : "FFI", leftPMtime : 133, isTeco: true),
            Project(name: "Project4, isTeco = false, shortname = MORN, leftPMTime = 444",shortname : "IFFCO", leftPMtime : 444, isTeco: false)
            ]
    @Binding var isSet: Bool
    let listOfPossibleHours: [Double] = [0.5,1,2,3,4,8]
    var body: some View {
        HStack {
            Text(projects[1].shortname)
                    Spacer()
            HourButton(id: "\(projects[1].shortname)\(0)", isSet: true, value: listOfPossibleHours[0])
            HourButton(id: "\(projects[1].shortname)\(1)", isSet: true, value: listOfPossibleHours[1])
            Text("\(projects[1].leftPMtime)")
                       }
                }
    }


struct HourButton: View,Identifiable {
   
    var id: String = "hour"
    @State var isSet: Bool
    var value: Double = 1
    
    var body: some View {
        HStack {
            Button {
                isSet.toggle()
                print("clic already \(isSet) \(id)h & \(value) has Value")
                
            } label: {
                Label("8h", systemImage: isSet ? "circle.fill" : "circle")
                    .labelStyle(.iconOnly)
                    .foregroundStyle(isSet ? .blue : .gray)
                
            }.id(id)
            
        }
        
        }
}

struct TimeKeyinList: View, Identifiable{
    var id: String
    var projects = [
        Project(name: "Project1, isTeco = false, shortname = MORN, leftPMTime = 131",shortname : "MORN", leftPMtime : 131, isTeco: false),
        Project(name: "Project2, isTeco = false, shortname = MORN, leftPMTime = 122",shortname : "IFF", leftPMtime : 122, isTeco: true),
        Project(name: "Project3, isTeco = false, shortname = MORN, leftPMTime = 133",shortname : "FFI", leftPMtime : 133, isTeco: true),
        Project(name: "Project4, isTeco = false, shortname = MORN, leftPMTime = 444",shortname : "IFFCO", leftPMtime : 444, isTeco: false)
    ]
    @State var isSet: [Bool]
    @State private var hideTeco = false
    
    
    var filteredProjects: [Project] {
        // removeTECO project
        projects.filter { project in
            !project.isTeco
        }}
    var body: some View {
        VStack {
            ForEach(Array(filteredProjects.enumerated()), id: \.offset) { (row, project) in
                TimeKeyInRow(id: project.shortname, projects: projects, isSet: $isSet[row])
            }
            }.labelsHidden()
        }
    }



struct Project: Identifiable {
    var id: UUID = UUID()
    var name: String
    var shortname: String
    var leftPMtime: Int
    var isTeco: Bool

}


struct TimeKeyInRow_Previews: PreviewProvider {
    static var previews: some View {
        //#Preview {
        let projects = [
                Project(name: "Project1, isTeco = false, shortname = MORN, leftPMTime = 131",shortname : "MORN", leftPMtime : 131, isTeco: false),
                Project(name: "Project2, isTeco = false, shortname = MORN, leftPMTime = 122",shortname : "IFF", leftPMtime : 122, isTeco: true),
                Project(name: "Project3, isTeco = false, shortname = MORN, leftPMTime = 133",shortname : "FFI", leftPMtime : 133, isTeco: true),
                Project(name: "Project4, isTeco = false, shortname = MORN, leftPMTime = 444",shortname : "IFFCO", leftPMtime : 444, isTeco: false)
                ]
        TimeKeyInRow(id: "test", projects: projects, isSet: .constant(true))
    }
}
struct HourButton_Previews: PreviewProvider {
    static var previews: some View {
        //#Preview {
        HourButton(id: "MORN", isSet: true, value: 3)
    }
}

struct TimeKeyinList_Previews: PreviewProvider {
    static var previews: some View {
        //#Preview {
        TimeKeyinList(id: "test",isSet: [true])
    }
}

Accepted Answer

You do not show how you initialise

    @State var isSet: [Bool]

Why do you repeat projects initialisation in TimeKeyInRow and in TimeKeyinList ? If I understand your intent, you should only pass a single project.

So, I have added an initialisation in .onAppear. This is just a quick test. Of course, you have to update isBool each time a isTeco toggles or each time you add a new Project.

That's just a quick fix, to show the principle.

extension String {  // To align text
    func paddedToWidth(_ width: Int) -> String {
        let length = self.count
        guard length < width else {
            return self
        }

        let spaces = Array<Character>.init(repeating: " ", count: width - length)
        return self + spaces
    }
}

struct TimeKeyInRow: View,Identifiable {
    var id: String
    var project: Project. // Just pass the project
    /* Do not redeclare
    var projects = [
        Project(name: "Project1, isTeco = false, shortname = MORN, leftPMTime = 131",shortname : "MORN", leftPMtime : 131, isTeco: false),
        Project(name: "Project2, isTeco = false, shortname = MORN, leftPMTime = 122",shortname : "IFF", leftPMtime : 122, isTeco: true),
        Project(name: "Project3, isTeco = false, shortname = MORN, leftPMTime = 133",shortname : "FFI", leftPMtime : 133, isTeco: true),
        Project(name: "Project4, isTeco = false, shortname = MORN, leftPMTime = 444",shortname : "IFFCO", leftPMtime : 444, isTeco: false)
    ] */
    
    @Binding var isSet: Bool
    let listOfPossibleHours: [Double] = [0.5,1,2,3,4,8]
    
    var body: some View {
        HStack {
            Spacer()    // ADDED
            Text(project.shortname.paddedToWidth(5)) // Text(projects[1].shortname)
                .font(.custom("Menlo", size: 16)) // <<-- To get proper alignment
            Spacer()
            ForEach(listOfPossibleHours, id: \.self) { hour in
                HourButton(id: "\(project.shortname)", isSet: isSet, value: hour)
            }
            /* replace this
            HourButton(id: "\(project.shortname)\(0)", isSet: isSet, value: listOfPossibleHours[0])
            HourButton(id: "\(project.shortname)\(1)", isSet: isSet, value: listOfPossibleHours[1])
             // What do you expect with  \(0)
             */
            Text("\(project.leftPMtime)")
                .font(.custom("Menlo", size: 16)) // <<-- To get proper alignment
            Spacer()    // <<-- ADDED
        }
    }
}


struct HourButton: View,Identifiable {
    
    var id: String = "hour"
    @State var isSet: Bool
    var value: Double = 1
    
    var body: some View {
        HStack {
            Button {
                isSet.toggle()
                print("clic already \(isSet) \(id)h & \(value) has Value")
                
            } label: {
                Label("8h", systemImage: isSet ? "circle.fill" : "circle")
                    .labelStyle(.iconOnly)
                    .foregroundStyle(isSet ? .blue : .gray)
                
            }.id(id)
            
        }
        
    }
}

struct TimeKeyinList: View, Identifiable { 
    var id: String = "" // to ease test
    var projects = [
        Project(name: "Project1, isTeco = false, shortname = MORN, leftPMTime = 131",shortname : "MORN", leftPMtime : 131, isTeco: false),
        Project(name: "Project2, isTeco = false, shortname = MORN, leftPMTime = 122",shortname : "IFF", leftPMtime : 122, isTeco: true),
        Project(name: "Project3, isTeco = false, shortname = MORN, leftPMTime = 133",shortname : "FFI", leftPMtime : 133, isTeco: true),
        Project(name: "Project4, isTeco = false, shortname = MORN, leftPMTime = 444",shortname : "IFFCO", leftPMtime : 444, isTeco: false)
    ]
    @State var isSet: [Bool] = [true, false, false, true]
    @State private var hideTeco = false
    
    var filteredProjects: [Project] {
        // removeTECO project
        projects.filter { project in
            !project.isTeco
        }}
    
    var body: some View {
        VStack {
            ForEach(Array(filteredProjects.enumerated()), id: \.offset) { (row, project) in
                TimeKeyInRow(id: project.shortname, project: project, isSet: $isSet[row])
            }
        }
        .labelsHidden()
        .onAppear() {   // <<-- Added
            for (row, project) in filteredProjects.enumerated() {
                isSet[row] = !project.isTeco
            }
        }
    }
}

struct Project: Identifiable {
    var id: UUID = UUID()
    var name: String
    var shortname: String
    var leftPMtime: Int
    var isTeco: Bool

}

Note: What is isTeco meaning ?

In some cases, neither shortName not isTeco match with label:

Project(name: "Project2, isTeco = false, shortname = MORN, leftPMTime = 122",shortname : "IFF", leftPMtime : 122, isTeco: true),

Is it on purpose ?

Hi @Claude31 once again I really appreciate the fact that you are supporting and helping me, I believe you bring me to the right direction as now my ad doesn't't crash anymore but somehting is strange I do not see the list of project in TimekeyinList, it's white full page. here is my updated code with as you explain only one projects [ list] creation under project,

I also have to create another project list under the previous otherwise preview cannot read the Project file.... I'm deeply lost in the UUID documentation as the UUID documentation is very very poor and swift is my first language programming therefore I do not really understand what I'm doing by init and by giving UUID, just try and get ride of the error message till it works lol.

Once again thanks for trying to { understand me, understand my problem and trying to fix my bugs.....) I wish I can return the favour one day I will make sure I can after all this learning session ^^


import Foundation
import SwiftUI

extension String {  // To align text
    func paddedToWidth(_ width: Int) -> String {
        let length = self.count
        guard length < width else {
            return self
        }

        let spaces = Array<Character>.init(repeating: " ", count: width - length)
        return self + spaces
    }
}

struct TimeKeyInRow: View,Identifiable {
    var id: String
    var project: Project
    
    @Binding var isSet: Bool
    let listOfPossibleHours: [Double] = [0.5,1,2,3,4,8]
    
    var body: some View {
        HStack {
            Spacer()    // ADDED
            Text(project.shortname.paddedToWidth(5)) // Text(projects[1].shortname)
                .font(.custom("Menlo", size: 16)) // <<-- To get proper alignment
            Spacer()
            ForEach(listOfPossibleHours, id: \.self) { hour in
                HourButton(id: "\(project.shortname)", isSet: isSet, value: hour)
            }
            Text("\(project.leftPMtime)")
                .font(.custom("Menlo", size: 16)) // <<-- To get proper alignment
            Spacer()    // <<-- ADDED
        }
    }
}


struct HourButton: View,Identifiable {
    
    var id: String = "hour"
    @State var isSet: Bool
    var value: Double = 1
    
    var body: some View {
        HStack {
            Button {
                isSet.toggle()
                print("clic already \(isSet) \(id)h & \(value) has Value")
                
            } label: {
                Label("8h", systemImage: isSet ? "circle.fill" : "circle")
                    .labelStyle(.iconOnly)
                    .foregroundStyle(isSet ? .blue : .gray)
                
            }.id(id)
            
        }
        
    }
}

struct TimeKeyinList: View, Identifiable {
    var id: String = "" // to ease test
    var projects = [Project]()
    @State var isSet: [Bool] = [true, false, false, true]
    @State private var hideTeco = false
    
    var filteredProjects: [Project] {
        // removeTECO project
        projects.filter { project in
            !project.isTeco
        }}
    
    var body: some View {
        VStack {
            ForEach(Array(filteredProjects.enumerated()), id: \.offset) { (row, project) in
                TimeKeyInRow(id: project.shortname, project: project, isSet: $isSet[row])
            }
        }
        .labelsHidden()
        .onAppear() {   // <<-- Added
            for (row, project) in filteredProjects.enumerated() {
                isSet[row] = !project.isTeco
            }
        }
    }
}

struct Project: Identifiable {
    var id: UUID = UUID()
    var name: String
    var shortname: String
    var leftPMtime: Int
    var isTeco: Bool
    
    var projects = [
        (id: UUID.init(), name: "Project1, isTeco = false, shortname = MORN, leftPMTime = 111",shortname : "MORN", leftPMtime : 111, isTeco: false),
        (id: UUID.init(), name: "Project2, isTeco = true, shortname = IFF, leftPMTime = 222",shortname : "IFF", leftPMtime : 222, isTeco: true),
        (id: UUID.init(), name: "Project3, isTeco = true, shortname = FFI, leftPMTime = 333",shortname : "FFI", leftPMtime : 333, isTeco: true),
        (id: UUID.init(), name: "Project4, isTeco = false, shortname = IFFCO, leftPMTime = 444",shortname : "IFFCO", leftPMtime : 444, isTeco: false)
    ]
    
    init(id: UUID, name: String, shortname: String, leftPMtime: Int, isTeco: Bool) {
        self.id = id
        self.name = "project1"
        self.shortname = "MORN"
        self.leftPMtime = 131
        self.isTeco = true
    }

}
    

struct TimeKeyInRow_Previews: PreviewProvider {
    static var previews: some View {
        //#Preview {
        var project: Project = Project(id: UUID.init(), name: "Project1", shortname: "MORN", leftPMtime: 131, isTeco: true)
        TimeKeyInRow(id: "test", project: project, isSet: .constant(true))
    }
}
struct HourButton_Previews: PreviewProvider {
    static var previews: some View {
        //#Preview {
        HourButton(id: "MORN", isSet: true, value: 3)
    }
}

struct TimeKeyinList_Previews: PreviewProvider {
    static var previews: some View {
        //#Preview {
       var isSet: [Bool] = [true, false, false, true]
        var projects = [
            Project(id: UUID.init(), name: "Project1, isTeco = false, shortname = MORN, leftPMTime = 111",shortname : "MORN", leftPMtime : 111, isTeco: false),
            Project(id: UUID.init(), name: "Project2, isTeco = true, shortname = IFF, leftPMTime = 222",shortname : "IFF", leftPMtime : 222, isTeco: true),
            Project(id: UUID.init(), name: "Project3, isTeco = true, shortname = FFI, leftPMTime = 333",shortname : "FFI", leftPMtime : 333, isTeco: true),
            Project(id: UUID.init(), name: "Project4, isTeco = false, shortname = IFFCO, leftPMTime = 444",shortname : "IFFCO", leftPMtime : 444, isTeco: false)
        ]
        TimeKeyinList(id: "test", projects: projects, isSet: isSet)
    }
}

@Claude31 , It's working my app is working now I mean what I was trying to do is working thanks for all your TIPS now actually the only things that was not working is the previous for timekeyinList.

Do you think you can explain to me why ? is it because of this var filter project ? I have try to add it into the preview but still the issues is coming that only if I change all the project to isTECO= true and keep only one project I see the TIMEkeyinList showing one line....


import Foundation
import SwiftUI

extension String {  // To align text
    func paddedToWidth(_ width: Int) -> String {
        let length = self.count
        guard length < width else {
            return self
        }

        let spaces = Array<Character>.init(repeating: " ", count: width - length)
        return self + spaces
    }
}

struct TimeKeyInRow: View,Identifiable {
    var id: String
    var project: Project
    
    @Binding var isSet: Bool
    let listOfPossibleHours: [Double] = [0.5,1,2,3,4,8]
    
    var body: some View {
        HStack {
            Spacer()    // ADDED
            Text(project.shortname.paddedToWidth(5)) // Text(projects[1].shortname)
                .font(.custom("Menlo", size: 16)) // <<-- To get proper alignment
            Spacer()
            ForEach(listOfPossibleHours, id: \.self) { hour in
                HourButton(id: "\(project.shortname)", isSet: isSet, value: hour)
            }
            Text("\(project.leftPMtime)")
                .font(.custom("Menlo", size: 16)) // <<-- To get proper alignment
            Spacer()    // <<-- ADDED
        }
    }
}


struct HourButton: View,Identifiable {
    
    var id: String = "hour"
    @State var isSet: Bool
    var value: Double = 1
    
    var body: some View {
        HStack {
            Button {
                isSet.toggle()
                print("clic already \(isSet) \(id)h & \(value) has Value")
                
            } label: {
                Label("8h", systemImage: isSet ? "circle.fill" : "circle")
                    .labelStyle(.iconOnly)
                    .foregroundStyle(isSet ? .blue : .gray)
                
            }.id(id)
            
        }
        
    }
}

struct TimeKeyinList: View, Identifiable {
    var id: String = "" // to ease test
    var listofprojects = [
        Project(name: "Project1, isTeco = true, shortname = MORN, leftPMTime = 111",shortname : "MORN", leftPMtime : 111, isTeco: true),
        Project(name: "Project2, isTeco = true, shortname = IFF, leftPMTime = 222",shortname : "IFF", leftPMtime : 222, isTeco: true),
        Project(name: "Project3, isTeco = true, shortname = FFI, leftPMTime = 333",shortname : "FFI", leftPMtime : 333, isTeco: true),
        Project(name: "Project4, isTeco = true, shortname = IFFCO, leftPMTime = 444",shortname : "IFFCO", leftPMtime : 444, isTeco: true)
    ]
    
    @State var isSet: [Bool] = [true, true, true, true]
    @State private var hideTeco = false
    
    var filteredProjects: [Project] {
        // removeTECO project
        listofprojects.filter { project in
            !project.isTeco
            
        }}
    
    var body: some View {
        VStack {
            ForEach(Array(filteredProjects.enumerated()), id: \.offset) { (row, project) in
                TimeKeyInRow(id: project.shortname, project: project, isSet: $isSet[row])
                
                
            }
            
        }
        .labelsHidden()
        .onAppear() {
            // <<-- Added
            for (row, project) in filteredProjects.enumerated() {
                isSet[row] = !project.isTeco
            }
        }
    }
}

struct Project: Identifiable {
    var id: UUID = UUID()
    var name: String
    var shortname: String
    var leftPMtime: Int
    var isTeco: Bool
}
    

struct TimeKeyInRow_Previews: PreviewProvider {
    static var previews: some View {
        //#Preview {
        let project: Project = Project(id: UUID.init(), name: "Project1", shortname: "MORN", leftPMtime: 131, isTeco: true)
        TimeKeyInRow(id: "test", project: project, isSet: .constant(true))
    }
}
struct HourButton_Previews: PreviewProvider {
    static var previews: some View {
        //#Preview {
        HourButton(id: "MORN", isSet: true, value: 3)
    }
}

struct TimeKeyinList_Previews: PreviewProvider {
    static var previews: some View {
        //#Preview {
        let projects = [
            Project(name: "Project1, isTeco = true, shortname = MORN, leftPMTime = 111",shortname : "MORN", leftPMtime : 111, isTeco: true),
            Project(name: "Project2, isTeco = true, shortname = IFF, leftPMTime = 222",shortname : "IFF", leftPMtime : 222, isTeco: true),
            Project(name: "Project3, isTeco = true, shortname = FFI, leftPMTime = 333",shortname : "FFI", leftPMtime : 333, isTeco: true),
            Project(name: "Project4, isTeco = false, shortname = IFFCO, leftPMTime = 444",shortname : "IFFCO", leftPMtime : 444, isTeco: false)
        ]
        TimeKeyinList(listofprojects: projects, isSet: [true])
    }
}


@Claude31 preview Is now working but the status here below is almost a copy past of the. above one which is kind of newby way, do you think is there a way to reduce this code into a much simpler code ?


struct TimeKeyinList_Previews: PreviewProvider {
    static var previews: some View {
        //#Preview {
        let listofprojects = [
            Project(name: "Project1, isTeco = true, shortname = MORN, leftPMTime = 111",shortname : "MORN", leftPMtime : 111, isTeco: false),
            Project(name: "Project2, isTeco = true, shortname = IFF, leftPMTime = 222",shortname : "IFF", leftPMtime : 222, isTeco: false),
            Project(name: "Project3, isTeco = true, shortname = FFI, leftPMTime = 333",shortname : "FFI", leftPMtime : 333, isTeco: false),
            Project(name: "Project4, isTeco = false, shortname = IFFCO, leftPMTime = 444",shortname : "IFFCO", leftPMtime : 444, isTeco: false)
        ]
        @State var isSet: [Bool] = [true, true, true, true]
        var filteredProjects: [Project] {
            // removeTECO project
            listofprojects.filter { project in
                !project.isTeco
                
            }}
        VStack {
            ForEach(Array(filteredProjects.enumerated()), id: \.offset) { (row, project) in
                TimeKeyInRow(id: project.shortname, project: project, isSet: $isSet[row])
            }
        }
        .labelsHidden()
        .onAppear() {
            // <<-- Added
            for (row, project) in filteredProjects.enumerated() {
                isSet[row] = !project.isTeco
            }
        }
    }
}

It works with the code I sent previously:

But you did not use what I proposed.

Problem is here:

  • why declare projects inside a single project ? That does not make sense, that must be global
  • what is this init() in Project: it sets the same short name for all ?
  • in TimeKeyinList, projects is not initialized
struct TimeKeyinList: View, Identifiable {
    var id: String = "" // to ease test
    var projects = [Project] ()
    @State var isSet: [Bool] = [true, false, false, true]
    @State private var hideTeco = false
    
    var filteredProjects: [Project] {
        // removeTECO project
        projects.filter { project in
            !project.isTeco
        }}
    
    var body: some View {
        VStack {
            ForEach(Array(filteredProjects.enumerated()), id: \.offset) { (row, project) in
                TimeKeyInRow(id: project.shortname, project: project, isSet: $isSet[row])
            }
        }
        .labelsHidden()
        .onAppear() {   // <<-- Added
            for (row, project) in filteredProjects.enumerated() {
                isSet[row] = !project.isTeco
            }
        }
    }
}

struct Project: Identifiable {
    var id: UUID = UUID()
    var name: String
    var shortname: String
    var leftPMtime: Int
    var isTeco: Bool
    
    var projects = [
        (id: UUID.init(), name: "Project1, isTeco = false, shortname = MORN, leftPMTime = 111",shortname : "MORN", leftPMtime : 111, isTeco: false),
        (id: UUID.init(), name: "Project2, isTeco = true, shortname = IFF, leftPMTime = 222",shortname : "IFF", leftPMtime : 222, isTeco: true),
        (id: UUID.init(), name: "Project3, isTeco = true, shortname = FFI, leftPMTime = 333",shortname : "FFI", leftPMtime : 333, isTeco: true),
        (id: UUID.init(), name: "Project4, isTeco = false, shortname = IFFCO, leftPMTime = 444",shortname : "IFFCO", leftPMtime : 444, isTeco: false)
    ]
    
    init(id: UUID, name: String, shortname: String, leftPMtime: Int, isTeco: Bool) {
        self.id = id
        self.name = "project1"
        self.shortname = "MORN"
        self.leftPMtime = 131
        self.isTeco = true
    }

}

So, the correct code is as follows (I modified slightly to show you how you could initialise projects:

struct TimeKeyInRow: View,Identifiable {
    var id: String
    var project: Project
    /* Do not redeclare, that would hide the global projects var
    var projects = [
        Project(name: "Project1, isTeco = false, shortname = MORN, leftPMTime = 131",shortname : "MORN", leftPMtime : 131, isTeco: false),
        Project(name: "Project2, isTeco = false, shortname = MORN, leftPMTime = 122",shortname : "IFF", leftPMtime : 122, isTeco: true),
        Project(name: "Project3, isTeco = false, shortname = MORN, leftPMTime = 133",shortname : "FFI", leftPMtime : 133, isTeco: true),
        Project(name: "Project4, isTeco = false, shortname = MORN, leftPMTime = 444",shortname : "IFFCO", leftPMtime : 444, isTeco: false)
    ] */
    
    @Binding var isSet: Bool
    let listOfPossibleHours: [Double] = [0.5, 1, 2, 3, 4, 8]
    
    var body: some View {
        HStack {
            Spacer()    // ADDED
            Text(project.shortname.paddedToWidth(5)) // Text(projects[1].shortname)
                .font(.custom("Menlo", size: 16)) // <<-- To get proper alignment
            Spacer()
            ForEach(listOfPossibleHours, id: \.self) { hour in
                HourButton(id: "\(project.shortname)", isSet: isSet, value: hour)
            }
            Text("\(project.leftPMtime)")
                .font(.custom("Menlo", size: 16)) // <<-- To get proper alignment
            Spacer()    // ADDED
        }
    }
}

struct TimeKeyinList: View, Identifiable {
    var id: String = "" // to ease test
    @State var projects = [Project] ()  // Now need a State var to be able to modify later
    @State var isSet: [Bool] = [true, false, false, true]
    @State private var hideTeco = false
    
    var filteredProjects: [Project] {
        // removeTECO project
        projects.filter { project in
            !project.isTeco
        }}
    
    var body: some View {
        VStack(spacing: 10) {
            ForEach(Array(filteredProjects.enumerated()), id: \.offset) { (row, project) in
                TimeKeyInRow(id: project.shortname, project: project, isSet: $isSet[row])
            }
        }
        .labelsHidden()
        .onAppear() {   // <<-- Added ; projects initialized here
            projects = [
                Project(name: "Project1, isTeco = false, shortname = MORN, leftPMTime = 131",shortname : "MORN", leftPMtime : 131, isTeco: false),
                Project(name: "Project2, isTeco = false, shortname = MORN, leftPMTime = 122",shortname : "IFF", leftPMtime : 122, isTeco: true),
                Project(name: "Project3, isTeco = false, shortname = MORN, leftPMTime = 133",shortname : "FFI", leftPMtime : 133, isTeco: true),
                Project(name: "Project4, isTeco = false, shortname = MORN, leftPMTime = 444",shortname : "IFFCO", leftPMtime : 444, isTeco: false)
            ]
            for (row, project) in filteredProjects.enumerated() {
                isSet[row] = !project.isTeco
            }
        }
    }
}

Note : if you want more space between rows, change :

    var body: some View {
        VStack(spacing: 10) {  // <<-- more spacing
            ForEach(Array(filteredProjects.enumerated()), id: \.offset) { (row, project) in
                TimeKeyInRow(id: project.shortname, project: project, isSet: $isSet[row])
            }
        }

@Claude31 now trying to put this back in my main project I believe I messsing aroung with this UUID things

I cannot parse my project anymore since I use this UUID as id instead of Int :

Before:

struct Project: Hashable, Codable,Identifiable {
    var id: Int

After


struct Project: Hashable, Codable,Identifiable {
    var id: UUID = UUID()

I have try to change my JSON for each project from

Before:

[
    {
        "name": "BM1 IFFCO JEDDAH 23",
        "shortname": "IFFCO",
        "leftPMtime": 123,
        "id": 1001,
        "isFeatured": false,

After


[
    {
        "name": "BM1 IFFCO JEDDAH 23",
        "shortname": "IFFCO",
        "leftPMtime": 123,
        "id": "Project1,1001"
        "isFeatured": false,

or

[
    {
        "name": "BM1 IFFCO JEDDAH 23",
        "shortname": "IFFCO",
        "leftPMtime": 123,
        "id":"Project1, isTeco = false, shortname = MORN, leftPMTime = 111"
        "isFeatured": false,

without any success as it seems on google UUID is a 4 bit ( apple doc too) and I aslo see this in your previous line of code here :


Project(name: "Project1, isTeco = false, shortname = MORN, leftPMTime = 131",shortname : "MORN", leftPMtime : 131, isTeco: false),

which seems new to me but maybe I'm making the like now , anyway all my attempts are failing with the following code.


  MyPmV1/ModelData.swift:67: Fatal error: Couldn't parse ProjectData.json as Array<Project>:
            dataCorrupted(Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 0", intValue: 0), CodingKeys(stringValue: "id", intValue: nil)], debugDescription: "Attempted to decode UUID from invalid UUID string.", underlyingError: nil))
        ]

sure this is due to the way I'm JSONning my data but I a link to a good website where I can learn or maybe sharing a solution would be awesome,

BTW I'm still trying to understand the difference between preview and sending to phone since we have to almost copy paste the code sometime.... In TimekeyinList for exemple

You should open a new thread, as it is another question.

And show how you generate and parse the JSON.

But, first, why do you need id here ?

struct TimeKeyinList: View, Identifiable { 
    var id: String = ""

This works:

struct TimeKeyinList: View { // , Identifiable {
    // var id: String = "" // to ease test
    @State var projects = [Project] () 

You should probably remove the id from the JSON, keep id as UUID and define codingKeys, except for id:

struct Project: Identifiable {
    var id: UUID = UUID()
    var name: String
    var shortname: String
    var leftPMtime: Int
    var isTeco: Bool

    private enum CodingKeys : String, CodingKey {
        case name, shortname, leftPMtime, isTeco
    }
}

See details here: https://stackoverflow.com/questions/64348019/using-uuid-with-json-in-swift

Hi @Claude31 I will start two other threat as I'm progressing thru my app thansk to all your learning and idea.

I have spotted this id: String in TimekeyinList was useless during my cleaning, I actually also spot that if I put your code into a LIST then the code is not working anymore strange behaviour, so I remove the list{} and use. a VStack instead as per your recaumentdation Spacing Vstack if what I needed.

As for how I parse my JSON it's exactly as per your link ,

here is the way I parse :```


//
//  ModelData.swift
//  MyPmV1
//
//  Created by Sebastien BENAVIDES on 3/2/24.
//

import Foundation
import Observation

@Observable
class ModelData {
    var projects: [Project] = load("ProjectData.json")
    var hikes: [Hike] = load("hikeData.json")
    var profile = Profile.default

    
    var features: [Project] {
           projects.filter { $0.isFeatured }
       }

    
    var categories: [String: [Project]] {
           Dictionary(
               grouping: projects,
               by: { $0.category.rawValue }
           )
       }
    
    var buisness: [String: [Project]] {
           Dictionary(
               grouping: projects,
               by: { $0.buisness_model.rawValue }
           )
       }
    var customers: [String: [Project]] {
           Dictionary(
               grouping: projects,
               by: { $0.customer.rawValue }
           )
       }
}



func load<T: Decodable>(_ filename: String) -> T {
    let data: Data


    guard let file = Bundle.main.url(forResource: filename, withExtension: nil)
    else {
        fatalError("Couldn't find \(filename) in main bundle.")
    }


    do {
        data = try Data(contentsOf: file)
    } catch {
        fatalError("Couldn't load \(filename) from main bundle:\n\(error)")
    }


    do {
        let decoder = JSONDecoder()
        return try decoder.decode(T.self, from: data)
      
    } catch {
        fatalError("Couldn't parse \(filename) as \(T.self):\n\(error)")
    }
}


``

I try to use UUID as per your recommendation and the mentioned of the. link below but code keys is way to much in term of string as to be decodable I need to use all the variables inside the project..... which is way to much and some of them are GPS coordinates. here is a glimpse of one project in my JSON


"name": "HOLIDAY",
        "shortname": "HOLIDAY",
        "leftPMtime": 999,
        "category": "Food & Baverage",
        "city": "TBD",
        "country": "Singapore",
        "buisness_model": "BM3",
        "state": "TBD",
        "id": 1014,
        "isSpecific": false,
        "isFeatured": false,
        "isFavorite": false,
        "isTeco": false,
        "customer": "Internal",
        "coordinates": {
            "longitude":  106.8451,
            "latitude":  -6.2146
        },
        "description": "To be written",
        "imageName": "TPLogo"

and here is a glimpse of my Projects.swift file


import Foundation
import SwiftUI
import CoreLocation

struct Project: Hashable, Codable,Identifiable {
    var id: Int
    var name: String
    var shortname: String
    var leftPMtime: Int
//    var city: String
//    var country: String
    var state: String
    var description: String
    var isFeatured: Bool
    var isSpecific: Bool
   var isFavorite: Bool
    var isTeco: Bool
    
    var category: Category
        enum Category: String, CaseIterable, Codable {
            case foodAndBaverage = "Food & Baverage"
            case NutritionAndBioscience = "Nutrition & Bioscience"
        }
    
    var buisness_model: Buisness
        enum Buisness: String, CaseIterable, Codable {
            case BM1 = "BM1"
            case BM2 = "BM2"
            case BM3 = "BM3"
        }
    
    var customer: CustomerCat
        enum CustomerCat: String, CaseIterable, Codable {
            case Nestle = "Nestle"
            case IFF = "IFF"
            case FFI = "FFI"
            case IFFCO = "IFFCO"
            case Abbott = "Abbott"
            case Mondelez = "Mondelez"
            case Internal = "Internal"

            
        }
    
    private var imageName: String
       var image: Image {
           Image(imageName)
       }
    
    var featureImage: Image? {
           isFeatured ? Image(imageName + "_feature") : nil
       }
    
    private var coordinates: Coordinates
    
    var locationCoordinate: CLLocationCoordinate2D {
          CLLocationCoordinate2D(
              latitude: coordinates.latitude,
              longitude: coordinates.longitude)
      }


        struct Coordinates: Hashable, Codable {
            var latitude: Double
            var longitude: Double
        }
}

I actually also spot that if I put your code into a LIST then the code is not working anymore strange behaviour

What do you mean ? What code do you pour where ? What do you get (error, crash, wrong result ?)

I have tested this, it works:

    var body: some View {
        List { //VStack(spacing: 10) {
            ForEach(Array(filteredProjects.enumerated()), id: \.offset) { (row, project) in
                TimeKeyInRow(id: project.shortname, project: project, isSet: $isSet[row])
            }
        }

If id is an Int in JSON, you can have an Int in Project for id as well:

struct Project: Identifiable {
    var id: Int = 0// UUID = UUID()
    var name: String
    var shortname: String
    var leftPMtime: Int
    var isTeco: Bool
    // other stuff

Just declare it when you load projects (either in code or you retrieve from JSON)

        .onAppear() {   // <<-- Added
            projects = [
                Project(id: 1, name: "Project1, isTeco = false, shortname = MORN, leftPMTime = 131",shortname : "MORN", leftPMtime : 131, isTeco: false),
                Project(id: 2, name: "Project2, isTeco = false, shortname = MORN, leftPMTime = 122",shortname : "IFF", leftPMtime : 122, isTeco: true),
                Project(id: 3, name: "Project3, isTeco = false, shortname = MORN, leftPMTime = 133",shortname : "FFI", leftPMtime : 133, isTeco: true),
                Project(id: 4, name: "Project4, isTeco = false, shortname = MORN, leftPMTime = 444",shortname : "IFFCO", leftPMtime : 444, isTeco: false)
            ]
            for (row, project) in filteredProjects.enumerated() {
                isSet[row] = !project.isTeco
            }
        }

Why do you declare as private:

    private var coordinates: Coordinates

You'd better do this:

struct Project: Hashable, Codable,Identifiable {
    var id: Int
    var name: String
    var shortname: String
    var leftPMtime: Int
    var state: String
    var description: String
    var isFeatured: Bool
    var isSpecific: Bool
    var isFavorite: Bool
    var isTeco: Bool
    var coordinates: Coordinates

Finally, you said

I try to use UUID as per your recommendation and the mentioned of the. link below but code keys is way to much in term of string as to be decodable I need to use all the variables inside the project..... which is way to much and some of them are GPS coordinates. here is a glimpse of one project in my JSON

Could you explain what the problem is, I don't understand your concern.

the issues with UUID in Project.swift is that when I change from id to UUID I get teh following message

Type 'Project' does not conform to protocol 'Decodable' the moment I add this

 private enum CodingKeys : String, CodingKey {
            case name, shortname, leftPMtime, isTeco
        }

and this code for every single line that is into the Project.swift

& 2. Cannot automatically synthesize 'Decodable' because 'id' does not have a matching CodingKey and does not have a default value
3. Cannot automatically synthesize 'Decodable' because 'state' does not have a matching CodingKey and does not have a default value
...
9. Cannot automatically synthesize 'Decodable' because 'category' does not have a matching CodingKey and does not have a default value

as for the list{ } if I add the List in this code , now it's even worth than before since I change all to Int even the id of button is now Int to match ID of the project all the button for every single row become active, but the moment I remove the list everything works fine

//
//  TimeKeyinList.swift
//  MyPmV1
//
//  Created by Sebastien BENAVIDES on 9/2/24.
//
import SwiftUI

struct TimeKeyinList: View {
   
    
    @Environment(ModelData.self) var modelData
    let projects = ModelData().projects // Now need a State var to be able to modify later I did not do that need to check with Claude
    @State var isSet: [Bool] = [false, false, false, false, false,false,false,false] // explain to claude that I I ahve more proejct in future and I don't increare this list then the app crash ... not very sure why but i believe it's due to the number of row link with the number of project dispaly
    
    var filteredNonTECOProjects: [Project] {
        // removeTECO project
        modelData.projects.filter { project in
            !project.isTeco
        }}
    

    var body: some View {
        VStack {
            HStack {
                Text("TIME KEY_IN")
                    .font(.title)
                    .multilineTextAlignment(.leading)
                    .bold()
                Spacer()
                Text("8h")
                    .font(.title)
                    .multilineTextAlignment(.leading)
                    .bold()
                
            }.padding()
            
            Spacer()
            
            
            VStack(spacing: 5){
                HStack(alignment: .top) {
                    
                    Text(" Project Name")
                        .multilineTextAlignment(.leading)
                        .lineLimit(2)
                        .frame(width: 120.0) // <<-- To get proper alignment
                    
                        Text(".5h")
                        Text("1h")
                        Text("2h")
                        Text("3h")
                        Text("4h")
                        Text("8h")
                    Text("Time Left")
                        .multilineTextAlignment(.trailing)
                        .lineLimit(2)
                        .frame(width: 50)
                }
                    
                
              
                VStack(spacing: 20) {
                    BlankKeyInRow()
                    ForEach(Array(filteredNonTECOProjects.enumerated()), id: \.offset) { (row, project) in
                        
                        if project .isSpecific {
                           Divider()
                            TimeKeyInRow(id: project.id, project: project, isSet: $isSet[row])
                        } else {
                            TimeKeyInRow(id: project.id, project: project, isSet: $isSet[row])
                        
                    }
                    
                    
                    }
                }
                

                            .listStyle(.inset)
                
                            DatePicker(selection: .constant(Date()), label: { Text("Date") })
            }
            
            
            
            .environment(\.sizeCategory, .extraExtraExtraLarge)
                .labelsHidden()
            Spacer()
                .onAppear() {
                    // <<-- Added
                    for (row, project) in filteredNonTECOProjects.enumerated() {
                            isSet[row] = !project.isTeco
                        }
                    }
            Spacer()
        }
    }
}

struct TimeKeyinList_Previews2: PreviewProvider {
    static var previews: some View {
        //#Preview

        TimeKeyinList().environment(ModelData())
    }
}

Creating an Array/Button with specific ID everybutiton regardless of projectName or Hours
 
 
Q