I am learning how to implement the new @SectionedFetchRequest with my Core Data object graph.
Here is my call...
@SectionedFetchRequest(
sectionIdentifier: \.sectionDay,
sortDescriptors: [
SortDescriptor(\.dateCommences, order: .reverse),
SortDescriptor(\.timeStampCreated, order: .reverse)],
animation: .default)
private var events: SectionedFetchResults<String, PTG_Event>
where PTG_Event
is the Core Data entity of results that I am expecting to be returned, sectionDay
is a convenience method that is prepared in an extension to PTG_Event
.
I have a user setting to display archived records (or not) using the entity attribute isArchived
and I am attempting to take advantage of the @AppStorage
wrapper as follows...
@AppStorage("preference_displayArchived") var kDisplayArchived = true
I cannot include a predicate in my sectioned fetch request, e.g.
predicate: NSPredicate(format: "isArchived == %@", NSNumber(booleanLiteral: kDisplayArchived)),
... because I receive the error ...
Cannot use instance member 'kDisplayArchived' within property initializer; property initializers run before 'self' is available
I understand this, but how do I create a dynamic reference to the app storage wrapper?
I am determined to figure out an easy method to use the user setting value preference_displayArchived
in the sectioned fetch request, so I have also attempted to hack a solution together using the method shown in #wwdc21-10017 for preparing a query for the .searchable
modifier, but have been so far unsuccessful in figuring out how to squeeze that into my List
code.
@State private var displayArchived = false
var queryArchived: Binding<Bool> {
Binding {
displayArchived
} set: { newValue in
displayArchived = newValue
events.nsPredicate = newValue == false ? nil : NSPredicate(format: "isArchived == %@", NSNumber(booleanLiteral: kDisplayArchived))
}
}
Any suggestions?
I'm not that pleased with this solution but it works (on iOS only) and until I can find a more elegant method (that also works on macOS), this will have to suffice!
Noting that...
@AppStorage("preference_displayArchived") var kDisplayArchived = true
Using the same call to @SectionedFetchRequest
in the question, I need to complete three tasks...
1 - Update the List when it appears and when the value to the preference changes to include a predicate that will effectively filter the SectionedFetchResults
(in this case SectionedFetchResults<String, PTG_Event>
):
var body: some View {
List() {
....
}
.onAppear() {
events.nsPredicate = predicateArchived
}
.onChange(of: kDisplayArchived) { _ in
events.nsPredicate = predicateArchived
}
}
2 - Add a computer property for predicateArchived
:
var predicateArchived: NSPredicate {
kDisplayArchived == true ? NSPredicate(value: true) : NSPredicate(format: "isArchived == %@", NSNumber(booleanLiteral: kDisplayArchived))
}
3 - Finally I also have to update the search to ensure that this app preference is adopted during the search. Building on the code presented to us in WWDC21-10017 (in presentation skip to time 21:29):
@State private var searchText = String()
var query: Binding<String> {
Binding {
searchText
} set: { newValue in
searchText = newValue
let predicate01 = NSPredicate(format: "name CONTAINS[cd] %@", newValue)
let predicate02 = NSPredicate(format: "reference CONTAINS[cd] %@", newValue)
let predicateArchived = kDisplayArchived == true ? NSPredicate(value: true) : NSPredicate(format: "isArchived == %@", NSNumber(booleanLiteral: kDisplayArchived))
let predicateOr = NSCompoundPredicate(orPredicateWithSubpredicates: [predicate01, predicate02])
let predicateAll = NSCompoundPredicate(andPredicateWithSubpredicates: [predicateArchived, predicateOr])
events.nsPredicate = newValue.isEmpty ? nil : predicateAll
}
}
NOTES
Known issue: after cancelling a search, the List
refreshes without a predicate applied. Need to develop a workaround for this inconsistency.