Home > OS >  SwiftUI Search Core Data With Relationships
SwiftUI Search Core Data With Relationships

Time:10-13

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
  • Related