I have a SwiftUI app with SwiftUI life cycle and am persisting data in Core Data. I am using Xcode 14.0.1 and iOS 16 to create a NavigationSplitView architecture. This all works fine. I have added .searchable to the main list and am able to search the entity string fields in the entity but I want to include string fields in the relationship entities and have not been able to do so.
Let's say I have an entity Trip, with name, description and comment attributes - all Strings. I create a searchResults var and use the result in the list. This works for the fields discussed.
var searchResults: [Trip] {
if searchText.isEmpty {
return Array(tripsFetched)
} else {
return Array(tripsFetched).filter {
$0.wrappedTripName.lowercased().contains(searchText.lowercased())
||
$0.wrappedTripDescription.lowercased().contains(searchText.lowercased())
||
$0.wrappedComment.lowercased().contains(searchText.lowercased())
}//filter
}//if else
}//var search results
Now let's say I have a one to many relationship between Trip and an entity Site and say Site has string attributes for siteName and siteDescription. I have not been able to add an iteration over the NSSet of Site objects to look for the searchText. I've made many attempts including the following but nothing has worked.
||
$0.sites?.allObjects(where: $0.wrappedSiteName.contains(searchText.lowercased()))
Any guidance would be appreciated.
CodePudding user response:
For searching we use the nsPredicate property on the fetch request.
You'll need an or predicate using the contains keyword for the text and equals with either the object or it's id for the relation, but this page shows all the different ways it can be configured:
https://developer.apple.com/documentation/foundation/nspredicate
There is a SwiftUI example on this page:
https://developer.apple.com/documentation/swiftui/fetchrequest/configuration
.onChange(of: query) { value in
quakes.nsPredicate = query.isEmpty
? nil
: NSPredicate(format: "place CONTAINS %@", value)
}
CodePudding user response:
For others - malhal is correct. You can create a compound predicate to search the one-to-many relationships. The important part is the syntax referenced in his links that uses the ANY keyword. This is an example:
func cdSearchQuery(searchText: String) {
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = Trip.fetchRequest()
let sortDescriptor = NSSortDescriptor(key: "tripName", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
let predicateTrip = NSPredicate(format: "tripName CONTAINS[cd] %@ || tripDescription CONTAINS[cd] %@ || tripComment CONTAINS[cd] %@ || tripCompanions CONTAINS[cd] %@", searchText, searchText, searchText, searchText)
//for the to-many attributes, use the ANY keyword
let predicateSite = NSPredicate(format: "ANY sites.siteName CONTAINS[cd] %@ || sites.siteDescription CONTAINS[cd] %@ || sites.siteLocation CONTAINS[cd] %@", searchText, searchText, searchText )
let predicate = NSCompoundPredicate(orPredicateWithSubpredicates: [predicateTrip, predicateSite])
fetchRequest.predicate = predicate
do {
let results = try context.fetch(fetchRequest)
//for testing - remove and send results to a Published array
if let tripArray = Array(results) as? [Trip] {
for t in tripArray {
print("included is \(String(describing: t.tripName))")
}
}
} catch {
print("Error retrieving compound predicate")
}
}//cd search