Boolean not updating in view but updating in struct

  Kiến thức lập trình

I’m quite new to SwiftUI and I am building an application that breaks down tasks into subtasks using AI, but I’m having issues displaying said Subtasks. The current functionality I want is for the user to be able to tick every subtask that they complete and it persists throughout the application, I am using SwiftData for this. I have the basic UI for the checklist implemented but the isCompleted property just does not want to toggle.

The toggle() function produces the same results but including the mutating function helps me to debug this.

I included debug prints in the code to debug the changes to the variable.

Here is the code for the task and subtask class/struct:

@Model
class BlendedTask: Codable, Identifiable {
    var id = UUID()
    let name: String
    var subtasks: [Subtask]
    
    var task: UserTask {
        return UserTask(id: id, name: name, duration: 3, startTime: nil, priority: .medium, imageURL: "tornado", details: nil, pomodoro: true, pomodoroCounter: 0, blended: true)
    }
    
    private enum CodingKeys: String, CodingKey {
        case id, name, subtasks
    }
    
    //  Initializers
    
    init(name: String, subtasks: [Subtask]) {
        self.name = name
        self.subtasks = subtasks
    }
    
    init() {
        self.id = UUID()
        self.name = ""
        self.subtasks = []
    }
    
    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        name = try container.decode(String.self, forKey: .name)
        subtasks = try container.decode([Subtask].self, forKey: .subtasks)
    }
}
// Subtask
struct Subtask: Codable, Identifiable, Hashable{
    var id = UUID()
    let name: String
    var details: [Detail]

    private enum CodingKeys: String, CodingKey {
        case id, name, details
    }
}
// Subtask details
struct Detail: Codable, Identifiable, Hashable {
    var id = UUID()
    var description: String
    var isCompleted: Bool
    
    init(description: String) {
        self.description = description
        self.isCompleted = false
    }
    
    mutating func toggleCompleted() {
        print("-struct OLD: (self.isCompleted)")
        self.isCompleted.toggle()
        print("-struct NEW: (self.isCompleted)")
        print("toggled for detail: (self.description)")
    }
}

And here is the implementation in the view to show the details:

struct BlendedTaskDetailView: View {
    @Environment(.dismiss) private var dismiss

    @State private var isAnimated = false
    @State var blendedTask: BlendedTask
    
    var body: some View {
        
        ZStack {
            Color.BG.ignoresSafeArea()
            VStack {
                Text("Task: " + blendedTask.name)
                    //modifiers
                
                List {
                    ForEach(Array($blendedTask.subtasks.enumerated()), id: .1.id) { index, $subtask  in
                        SubtaskCell(subtask: $subtask, taskNo: index + 1)
                    }
                    .listRowBackground(Color.faPurple)
                }
                // Modifiers
            }
            .padding(.top, 30)
            
        }
    }
}
struct SubtaskCell: View {
    @Binding var subtask: Subtask
    let taskNo: Int
    
    @State private var isAnimated = false
    
    var body: some View {
        VStack(alignment: .center) {
            Text("Task (taskNo): (subtask.name)")
                // modifiers
            
            ForEach(subtask.details.indices, id: .self) { j in
                DetailRow(detail: $subtask.details[j])
            }
            
        }
        //modifiers
    }
}
struct DetailRow: View {
    @Binding var detail: Detail
    @State private var isAnimated = false
    var body: some View {
        HStack {
            VStack(alignment: .leading) {
                Text(detail.description)
            }
            Spacer()
            
            Button(action: {
                withAnimation {
                    print("-view OLD: (detail.isCompleted)")
                    detail.toggleCompleted()
                    print("-view NEW: (detail.isCompleted)")
                }
            }, label: {
                Image(systemName: detail.isCompleted ? "checkmark.circle.fill" : "circle")
                    .contentTransition(.symbolEffect(.replace))
                
            })
            .frame(width: 35, height: 35)
            .foregroundStyle(detail.isCompleted ? .green : .white)
            
        }
       //modifiers
    }
}

Here is what the UI looks like:

Here is what the UI looks like:

This is what is printed when a button is pressed:

This is what is printed when a button is pressed:

New contributor

user10708060 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.

1

LEAVE A COMMENT