SwiftUI and FetchRequest : delay the reordering of a list

Viewed 248

I have written an application displaying a list of students. I use a List of NavigationLink for that purpose. The students are ordered by one of their properties questionAskedClass which is an integer. All this information is stored within CoreData.

@Environment(\.managedObjectContext) var managedObjectContext
@FetchRequest(entity: Student.entity(),
    sortDescriptors: [NSSortDescriptor(keyPath: \Student.questionAskedClass,
                                       ascending: true)])
var students: FetchedResults<Student>

var body: some View {
  NavigationView {
    List {
      ForEach(students) {student in
        NavigationLink(destination: StudentView(student: student)) {
          HStack {
            VStack(alignment: .leading) {
              Text("\(student.firstName)").font(.headline).foregroundColor(Color.black)
              Text("\(student.lastName)").font(.headline).foregroundColor(Color.gray)
            }
          }
        }
      }
    }
  }
}

When I press the name of the students, I switch to a new view called StudentView where I can get more information about the student and where I can update the property questionAskedClass

struct StudentView: View {
  @Environment(\.managedObjectContext) var managedObjectContext


  func askQuestion() {
    self.managedObjectContext.performAndWait {
      self.student.questionAskedClass += 1
      try? self.managedObjectContext.save()
    }
  }
}

Unfortunately, when I change that property, the ordering of the initial list is changed and I am taken away from the StudentView. The framework seems to get the feeling that the list needs to be reordered. But I just want this list to be reordered when I go back to the list. Not immediately when I change the value of questionAskedClass.

What can I do to mitigate this problem?

Thanks for your help.

1 Answers

You can try creating a simple NSFetchRequest<Student> and use the result of this fetch to update your students list.

@State var students: [Student] = []

fun refresh() {
    let fetchRequest = NSFetchRequest<Student>(entityName: "Student")
    students = try? managedObjectContext.fetch(fetchRequest) ?? []
}

You can trigger this refresh in onAppear so the list will be updated every time the View appears:

NavigationView {
    ...
}.onAppear {
    self.refresh()
}

Alternatively you can save your context in onAppear of the main view instead of StudentView:

struct StudentView: View {
    func askQuestion() {
        self.student.questionAskedClass += 1
    }
}

NavigationView {
    ...
}.onAppear {
    try? self.managedObjectContext.save()
}

If you want your data to persist when the app is terminated, add this function in AppDelegate (assuming your persistentContainer is declared there:

func applicationWillTerminate(_ application: UIApplication) {
    try? persistentContainer.viewContext.save()
}
Related