UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

SOLVED: Core Data: NSPredicate with array values as argument

Forums > SwiftUI

I'm trying to write a NSPredicate to filter my Students based on their tags.

Student CoreData entity has an NSSet of Tags (to many relationship):

extension Student {
   @NSManaged public var tags: NSSet?

And Tag CoreData entity has the inverse to-many relationship as well:

extension Tag {
   @NSManaged public var students: NSSet?

I use this request to select tags for filtering, based on a query String:

let dataController = DataController.shared
let request: NSFetchRequest<Tag> = Tag.fetchRequest()
request.predicate = NSPredicate(format: "%K CONTAINS[cd] %@", #keyPath(Tag.title), query)
let moc = dataController.container.viewContext
let fetchedTags = try? moc.fetch(request)

I get this way an optional array of tags ([Tag]?), or an empty one. That's working ok.

But, and that is the key point of my question, now I'm trying to use this array of tags to filter which students have that tags, this way:

tagPredicate = NSPredicate(format: "%K IN %@", #keyPath(Student.tags), fetchedTags!)

But it fails, with the error "NSInvalidArgumentException', reason: 'unimplemented SQL generation for predicate".

I'm trying to use different argument sintaxes, as detailed here: https://nspredicate.xyz, but neither "SELF CONTAINS", nor "IN" options don't work.

Does anybody knows how to get it working?

Thanks!

   

hi Óscar,

once you have a Tag object, say var tag: Tag, there's no need to go back directly to Core Data to get students associated with the object; those students are just

let students: Set<Student> = (tag.students as? Set<Student>) ?? []

if you have an optional array of Tags, which you called fetchedTags, then you can find all associated students for each element of the array

let associatedStudents: [Set<Student>] = fetchedTags?.compactMap({ $0 as? Set<Student> }) ?? []

finally, if you want to accumulate all of these sets together into a single Set of students, you could use reduce, starting with an empty set and then adding in (by union) each element of the associatedStudents array.

let studentCollection = associatedStudents.reduce(Set<Student>()) { $0.union($1) }

now (i think i have the syntax right ... it's my first code-writing exercise of the day!), you have all students who are associated with one of more of the fetchedTags.

hope that helps,

DMG

   

Thanks! Great information for me! I'm now reorganizing many parts of my app, thanks to your first line sintax, avoiding so many "FetchRequests" to filter data, and using your approach. Fantastic!

In the meanwhile, making advances with tag filtering, fot the second line you suggest:

let associatedStudents: [Set<Student>] = fetchedTags?.compactMap({ $0 as? Set<Student> }) ?? []

I get the "error" (just yellow indicator):

Cast from 'Tag' to unrelated type 'Set<Student>' always fails

Maybe is this the correct sintax? (Googling your proposals):

let associatedStudents: [Set<Student>] = fetchedTags?.compactMap { item in
        return item.students
    } as! [Set<Student>]

   

hi,

sorry ... as i said, first code writing exercise of the day ... i knew something was probably missing.

it should be

let associatedStudents = fetchedTags?.compactMap({ $0.students as? Set<Student> }) ?? []

it's not clear to me that you need to include the explicit type annotation that i suggested, but you can check.

hope that's right (typing from my iPad),

DMG

   

Hacking with Swift is sponsored by RevenueCat

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.