2

I have three files: AddAssignment, ViewAssignment and Task. A button is clicked on AddAssignment.swift which successfully appends form data to an array inside of Task.swift.

However, in a file AddAssignment.swift, where each item in this array is passed through a loop, the view fails to update with the newly appended data.

AddAssignment.swift

import SwiftUI

struct AddAssignment: View {
    
    @State var taskName = ""
    @State var dueDate = ""
    @State var subject = ""
    @State var weighting = ""
    @State var totalMarks = ""
    @State var buttonClicked = false
    
    var body: some View {
        NavigationView {
            
            VStack {
                
                HStack { // Titling
                    Spacer()
                    Text("Add New Task")
                        .font(.headline)
                        .scaleEffect(2.0)
                    Spacer()
                    Image(systemName: "plus.square.fill")
                        .scaleEffect(2.0)
                    Spacer()
                }
                
                
                Form { // Gathering data
                    Section {
                        TextField("Enter task name",
                                  text: $taskName)
                        TextField("Enter due date",
                                  text: $dueDate)
                        TextField("Enter subject",
                                  text: $subject)
                    }
                    
                    Section(header: Text("Enter other task details:")) {
                        TextField("Enter task weighting",
                                  text: $weighting)
                        TextField("Enter total marks",
                                  text: $totalMarks)
                    }
                }
                
                if buttonClicked == true {
                    Text("Task Created")
                        .font(.subheadline)
                        .foregroundColor(.green)
                }
                
                Button(
                    action: {
                        data.append(Task(taskName: self.taskName,
                                         dueDate: self.dueDate,
                                         subject: self.subject,
                                         weighting: self.weighting,
                                         totalMarks: self.totalMarks))
                        buttonClicked = true
                    },
                    label: {
                        Text("Create New Task")
                            .frame(width: 250,
                                   height: 50,
                                   alignment: .center)
                            .background(Color.blue)
                            .foregroundColor(.white)
                            .cornerRadius(8)
                    })
                    .padding()
            }
        }
        
        
        
        
    }
    
}


struct AddAssignment_Previews: PreviewProvider {
    static var previews: some View {
        AddAssignment()
            .preferredColorScheme(.light)
    }
}

Task.swift (logic)

import SwiftUI

struct Task: Identifiable {
    var id: String = UUID().uuidString
    var taskName: String
    var dueDate: String
    var subject: String
    var weighting: String
    var totalMarks: String
}

var data: [Task] = [
        Task(taskName: "Sample Task", dueDate: "1 Jan", subject: "Subject", weighting: "100", totalMarks: "100"),
        Task(taskName: "Sample Task 2", dueDate: "2 Jan", subject: "Subject B", weighting: "100", totalMarks: "100"),
        Task(taskName: "Sample Task 3", dueDate: "3 Jan", subject: "Subject C", weighting: "100", totalMarks: "100"),
    ]

ViewAssignment.swift (where my error is occurring:)

import SwiftUI

struct ViewAssignment: View {
    var body: some View {
        NavigationView {
            List(data) { task in
                NavigationLink (
                    destination: TaskDetailView(),
                    label: {
                        Image(systemName: "doc.append.fill")
                            .scaleEffect(2.5)
                            .padding()
                        
                        VStack(alignment: .leading, spacing: 3) {
                            
                            Text(task.taskName)
                                .fontWeight(.semibold)
                                .lineLimit(2)
                            
                            Text(task.dueDate)
                                .font(.subheadline)
                                .foregroundColor(.secondary)
                        }
                    })
                
                
            }
            .navigationTitle("My Tasks")
            
        }
        
    }
}




struct ViewAssignment_Previews: PreviewProvider {
    static var previews: some View {
        ViewAssignment()
    }
}


fesari
  • 33
  • 7

1 Answers1

0

You need to look at MVVM programming paradigm which is heavily used with SwiftUI.

So for your example you'd have your model:

struct Task: Identifiable {
    var id: String = UUID().uuidString
    var taskName: String
    var dueDate: String
    var subject: String
    var weighting: String
    var totalMarks: String
}

Your view:

struct ViewAssignment: View {
    
    // Observed to update you UI
    @ObservedObject var assignment = Assignments()
    
    var body: some View {
        NavigationView {
            VStack {
                List(self.assignment.data) { task in
                    NavigationLink (
                        destination: AdjustMe(),
                        label: {
                            Image(systemName: "doc.append.fill")
                                .scaleEffect(2.5)
                                .padding()
                            
                            VStack(alignment: .leading, spacing: 3) {
                                
                                Text(task.taskName)
                                    .fontWeight(.semibold)
                                    .lineLimit(2)
                                
                                Text(task.dueDate)
                                    .font(.subheadline)
                                    .foregroundColor(.secondary)
                            }
                        })
                }

                // Alter your model by calling its functions
                Button(action: {
                    self.assignment.addData()
                }) {
                    Text("Add Data")
                }
            }
            
            .navigationTitle("My Tasks")
        }
        
    }
}

And (important!) your class which wraps all the functions to alter your model.

// Must be ObservalbeObject
class Assignments: ObservableObject {
    // Everything that gets adjusted and needs UI update has to be marked published
    @Published var data: [Task] = [
            Task(taskName: "Sample Task", dueDate: "1 Jan", subject: "Subject", weighting: "100", totalMarks: "100"),
            Task(taskName: "Sample Task 2", dueDate: "2 Jan", subject: "Subject B", weighting: "100", totalMarks: "100"),
            Task(taskName: "Sample Task 3", dueDate: "3 Jan", subject: "Subject C", weighting: "100", totalMarks: "100"),
    ]
    
    // Function to alter your model/ view
    func addData() {
        self.data.append(Task(taskName: "Sample Task 4", dueDate: "5 Jan", subject: "Subject D", weighting: "200", totalMarks: "200"))
    }
}
Simon
  • 1,754
  • 14
  • 32
  • Hi, thanks for the response. I am kind of familiar with using ObservableObject, but would I be able to use this method in the AddAssignment view, so that when the button is clicked, a new list item is created based on what is entered into the text field? – fesari Jul 26 '21 at 12:02
  • You can access the object anywhere you declare it or send it to. If this model is used throughout your app make it an environment object. Like that you can call the same instance from everywhere and dont need to pass it around. Like so on ContentView().environmentObject(YourClass()) – Simon Jul 26 '21 at 12:11
  • 1
    Ahh good idea. I attached an environmentObject attribute to my ContentView file which was a tab view for all my screens, and so I was able to use the same instance across all menus. Thank you so much for the help! – fesari Jul 26 '21 at 12:30