skip to Main Content

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.

2

Answers


  1. Chosen as BEST ANSWER

    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
    

  2. 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)
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search