CoreStore sectioned list monitor how to specify .where clause in runtime

338 Views Asked by At

I have this code in my init method:

self.monitor = CoreStore.monitorSectionedList(
        From<ListEntityType>()
            .sectionBy(#keyPath(ListEntityType.muscle.name)) { (sectionName) -> String? in
                return "\(String(describing: sectionName)) years old"
            }
            .orderBy(.ascending(#keyPath(ListEntityType.muscle.name)), .ascending(\.name))
    )

I want to somehow add to this monitor .where condition in runtime.

ListEntityType is a type alias of an entity called Exercise. So each Exercise contains one to one relationship Muscle (exercise.muscle).

each entity has unique identifier attribute.

I have array of muscles identifier which I want to use as filter to display object from sectioned list.

How can loop through this identifiers to add then to .where clause to the CoreStore.monitorSectionedList

I probably expect something like this but not 100% sure just assumption:

let predicate = NSPredicate(format: "%K = $ARGUMENT")

    var predicates = [NSPredicate]()

    if let muscles : [MuscleGroupEntity] = workout?.muscles.toArray() {
        for muscle in muscles
        {
            let myNewPredicate = predicate.withSubstitutionVariables(["ARGUMENT" : muscle.id])
            predicates.append(myNewPredicate)
        }
    }

    self.monitor = CoreStore.monitorSectionedList(
        From<ListEntityType>()
            .sectionBy(#keyPath(ListEntityType.muscle.name)) { (sectionName) -> String? in
                return "\(String(describing: sectionName)) years old"
            }
            .where(format:"%K = %@", argumentArray: predicates)
            .orderBy(.ascending(#keyPath(ListEntityType.muscle.name)), .ascending(\.name))
    )

this code crashed with error:

-[NSComparisonPredicate rangeOfString:]: unrecognized selector sent to

I guess this is something from predicate I found here, but not sure how to use it correctly in my case

EDITED For MartinM comments:

I have ExerciseEntity with a property Muscle.

If I want to get all exercises using just one muscle id it pretty simple:

.where(format:"%K = %@", #keyPath(ExerciseEntity.muscle.id), "id_12345")

But I want to specify more muscles and define them in runtime. Somehow I need to understand what the format is and what the arguments and how to pass array of identifiers instead just one "id_12345"

2

There are 2 best solutions below

5
MartinM On BEST ANSWER

If you want to fetch all ExerciseEntity where their muscle.id is contained in a list of muscleIds, you simply use:

let muscleIds = workout?.muscles.compactMap({ $0.id }) ?? []
From....
   .where(Where<ExerciseEntity>(#keyPath(ExerciseEntity.muscle.id), isMemberOf: muscleIds))

Thats the equivalent of a predicate with format: %K IN %@, ExerciseEntity.muscle.id, muscleIds

This also works negating -> NOT(%K IN %@), same as you can use a !Where in CoreStore.

Compound predicates need to specify how they are linked. CoreStore supports a shorthand for 2 predicates at a time, using logical operators, for example:

Where<ExerciseEntity>(yourWhereClause) && Where<ExerciseEntity(otherWhereClause)

This is the equivalent to using NSCompoundPredicate like this:

NSCompoundPredicate(type: .and, subpredicates: [left.predicate, right.predicate])

You can also pass that compound predicate as argument to the where clause directly if you need a more complex conjunction. You could then also store that compound predicate somewhere and append another compound predicate that fits your needs.

5
Jon Rose On

I looks like the coreStore is a cool wrapper for core-data and there 'where' method is creating an NSPredicate behinds the scenes. The problem is that you are passing an array of predicates into a method that expects a string.

First turn your array of predicates into a single predicate. use NSCompoundPredicate andPredicateWithSubpredicates (see https://developer.apple.com/documentation/foundation/nscompoundpredicate/1407855-init).

Next, instead of passing it via a string using format, just pass that directly to the where() clause without the word format. I believe that CoreStore accept NSPredicates directly without having to create them from a format.

I think this will work:

 .where(NSCompoundPredicate.init(andPredicateWithSubpredicates: predicates))