Xcode beta 5 introduced @FetchRequest
for SwiftUI.
I have a View, which has a @FetchRequest
. The NSFetchRequest
is created within a manager that makes a static instance of itself available to property wrappers (they cannot use self). The same instance is passed to the view at creation.
What I want is for the FetchRequest
to be updated, when self.manager.reverse.toggle()
is called, in order for the view to change its ordering of objects. Unfortunately it seems like Manager.fetchRequest()
is only called once and then never again, even when I create new objects and transition between different views.
I am looking for suggestions on how to modify a fetch request that is made with the new property wrapper, so that I can resort my objects based on user actions.
struct MainView: some View {
@ObservedObject var manager: Manager
@FetchRequest(fetchRequest: Manager.shared.fetchRequest())
var ts: FetchedResults
var body: some View {
NavigationView {
List(ts, id: \.self) { t in
Text(t.name)
}
}.navigationBarItems(trailing:
Button(action: {
self.manager.reverse.toggle()
}) { Text("Reverse")}
}
}
final class Manager: ObservableObject {
@Published var reverse: Bool = false
// Since property wrappers cannot use self, we make a shared instance
// available statically. This same instance is also given to the view.
public static let shared = Manager()
func fetchRequest(reverse: Bool = false) -> NSFetchRequest<T> {
let request: NSFetchRequest<T> = T.fetchRequest()
request.sortDescriptors = [
NSSortDescriptor(
key: "name",
ascending: !self.reverse
)
]
return request
}
}
This is a very good question! I have been trying to do this and other very similar things with little success. I'm really hoping that this issue will get a suitable resolution in an upcoming beta. However, in the meantime, it is possible to switch between two @FetchRequests to accomplish what you wish to do.
Below is working code from a sample App I'm working on. I use a Button to toggle a Boolean @State, then use that state to pick the appropriate @FetchResults. I don't really like it, but it actually works quite well.
import SwiftUI
import CoreData
struct EntityListView : View {
@Environment(\.editMode) var editMode
@State var sortAscending: Bool = true
@Environment(\.managedObjectContext) var context: NSManagedObjectContext
@FetchRequest(entity: Entity.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Entity.order, ascending: true)])
var ascendingEntities: FetchedResults<Entity>
@FetchRequest(entity: Entity.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Entity.order, ascending: false)])
var descendingEntities: FetchedResults<Entity>
var body: some View {
NavigationView {
List() {
Section(header: Text("All Entities".uppercased()))
{
ForEach(sortAscending ? ascendingEntities : descendingEntities) { entity in
NavigationLink(destination: EntityEditView(entity: entity)) {
HStack {
Text(entity.name)
.font(.headline)
Spacer()
Text(String(entity.order))
.font(.subheadline)
.foregroundColor(.gray)
}
}
}
.onMove(perform: self.move)
.onDelete(perform: self.delete)
}
HStack {
Spacer()
Button(action: { self.sortAscending.toggle() }) { Text("Reverse Sort") }
Spacer()
}
}
.listStyle(GroupedListStyle())
.navigationBarTitle(Text("Entities"), displayMode: .large)
.navigationBarItems(trailing: EditButton() )
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With