I use a CoreData Model where a Group Object has GroupMembers(firstName: String) connected with the entity members. GroupMembers have a corresponding property group that connects back to the Group Object. In my Detailview I transfer an group Object. In a FetchRequest I want to filter all GroupMembers that have the same Group as my transfered group.
I use the following code:
struct GroupDetailView: View {
@ObservedObject var group: Group
@FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \GroupMember.firstName_, ascending: true)],
predicate: NSPredicate(format: "group == %@", group),
animation: .default)
private var members: FetchedResults<GroupMember>
var body: some View {
}
}
But I get the following error:
Cannot use instance member 'group' within property initializer; property initializers run before 'self' is available
CodePudding user response:
It's not straightforward at the moment. You could experiment with defaulting to a false predicate (so no results are returned) and then setting the predicate in onAppear
and onChange(group)
. Or perhaps in task(id: group)
which should do both.
.onAppear {
members.nsPredicate = NSPredicate(format: "group == %@", group)
}
However, this has the major flaw that if your GroupDetailView
is init again (without the group changing) the dynamically set predicate is lost. Sadly this is a major design flaw in @FetchRequest
, it seems to me they expected it to always be used in a top level ContentView
that would never be re-init, i.e. before we got the SwiftUI App Lifecycle.
A hacky workaround I've had some success with is to set the predicate at the top of body
, that way it always has the correct predicate. We are not supposed to update state from inside body but you'll notice the comment on nsPredicate
setter is marked as nonmutating so maybe it is ok. Give this a try and see if it works for you:
struct GroupDetailView: View {
let group: Group
@FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \GroupMember.firstName, ascending: true)],
predicate: NSPredicate(value: false), // this essentially stops the fetch from working bypassing the database.
animation: .default)
private var members: FetchedResults<GroupMember>
var body: some View {
let _ = members.nsPredicate = NSPredicate(format: "group == %@", group)
ForEach(members) { member in {
}
}
Another, workaround would be to move the @FetchRequest
into the parent View and update the predicate when a group is selected and pass the results (i.e. the wrappedValue) into your GroupDetailView
however that has the same problem that if the parent of that parent view is re-init then the predicate is still lost.
Another possible way (I have not tried yet) is to use the @FetchRequest(fetchRequest:)
initialiser, passing in an NSFetchRequest
object that is a global or a singleton and configured from an action in the parent View. That way it's predicate won't be lost. However that goes against the design of the new feature to dynamically update the predicate.