keypath objectID not found in entity exception - ios

I'm facing this strange issue when trying to fetch some objects after their objectID. The error complains that there is no such thing as an objectID key path, but the managed object should respond to that. The error is not thrown all the time, which makes me think it could be a concurrency problem, although I've double checked and each context performs the operations on its own thread.
Here is the predicate, although it looks sane to me:
[NSPredicate predicateWithFormat:#"objectID == %#", book.objectID]
Edit: Regarding this answer. I didn't tried using the object itself, I need to use the objectID because of multithreading considerations.

The answer given in THIS link still holds here.
It holds in the sense that you use the same predicate:
NSPredicate* p = [NSPredicate predicateWithFormat:#"SELF = %#",book.objectID];
But you supply the objectID as parameter.
CoreData allow comparison of objectIDs and objects when fetching from the store. it probably has to do with the fact that an objectID is used for a one-to-one mapping to a managed object (hence the use of SELF which is a predicate reserved word and not a property name).

Related

Is it possible to include a [NSPredicate predicateWithBlock:] in a NSCompoundPredicate? [duplicate]

I am playing with an app that uses Core Data and NSManagedObjects to populate a UITableView. There is only one class in my application, called Event. I have created the following custom instance method on Event:
- (BOOL)isExpired {
return ([[self.endOn dateAtEndOfDay] timeIntervalSinceNow] < 0);
}
I would like to limit the UITableView that displays Event objects to only the Events that are expired - that is, where isExpired returns YES. I have tried to do this by adding an NSPredicate to the NSFetchRequest:
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary * bindings) {return([evaluatedObject isExpired]);}];
[fetchRequest setPredicate:predicate];
but I get the error: *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Problem with subpredicate BLOCKPREDICATE(0x272ac)'
***. Does this mean that you can't use a block predicate with an NSFetchRequest? Or have I just constructed it improperly?
Thank you!
So, it appears that we've established in the comments to the original post that this is likely caused by SQLite stores being incompatible with block predicates, since Core Data cannot translate these to SQL to run them in the store (thanks, JoostK).
There might be a couple of ways to overcome this:
Provided that the end date of your entities is a regular attribute, you might be able to express the expiry constraint as a predicate format string instead of a block predicate, which Core Data should be able to translate into a SQL clause.
If the above is possible, you will probably prefer to use a fetch request template to retrieve the expired items. You would need to pass in a substitution variable like $NOW to give access to the current date, though. This has the advantage of making the predicate template show up in the model editor.
Both approaches, however, have the disadvantage of duplicating existing functionality (i.e., your isExpired method). So another way would be fetch all qualifiying entities regardless of their expiry state first, and then run a dedicated filtering step on the resulting set of entities to weed out the non-expired ones. Since by that point, they have been fully resurrected from the store, you should be able to use a block predicate for this.
You can do a normal fetch request without specifying the predicate, and afterwards filter the resulting array:
NSArray *allEvents = [context executeFetchRequest:fetchRequest];
if (!allEvents) { // do error handling here
}
NSArray *expiredEvents = [allEvents filteredArrayUsingPredicate:predicate];

NSManagedObjectContext -save: crashes when using NSPredicate

I have a search bar and some filters in my app that create two predicates. One for the string in the search bar and one for the filter (UISegmentedControl).
When the predicates are used and I insert one object into the context and then call -save: the app crashes with:
CoreData: error: Serious application error. Exception was caught during Core Data change processing. This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification. Can't use in/contains operator with collection 1379410732 (not a collection) with userInfo (null)
at the point of saving:
This does not happen when the predicates are not used.
Can I somehow remove the predicates during the process of saving? I already tried it by getting the NSFetchRequest, but I cannot set it back to the controller then.
Edit
I also tried to add self as an observer on the NSManagedObjectContextDidSaveNotification and then use -mergeChangesFromContextDidSaveNotification: like one discussion on SO suggested. This didn't help either.
I found my problem. It was quite easy, but the error message was so confusing that I didn't look at the right point.
The problem was that I was using [NSPredicate predicateWithFormat:#"number CONTAINS[cd] %#", string].
So the predicate was using CONTAINS on a NSNumber. The confusing part is that the predicates are working when querying existing data from the database. I still don't understand this part, but it's working now with == comparator.

NSPredicate not working with abstract entity attributes in core data

I have a core data class, SSSLicense that has attributes like name and type. It inherits from an abstract entity called SSSArchivableEntity which has a boolean attribute named isArchived (among others).
I've already fetched the full set of license entities from the DB and am now trying to filter based on type and the isArchived flag. However, despite many variations of my predicate, I cannot get a valid result.
Here is the relevant code:
NSSet *licenses = [person licenseList] ;
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(licenseType like %#) AND (isArchived == NO)",
lType];
NSSet *filteredLicenses = [licenses filteredSetUsingPredicate:predicate];
If I change my predicate to use another attribute of SSSLicense, say name (instead of isArchived), the predicate works. I even added a simple boolean attribute to SSSLicense and filtered using it successfully.
It seems like it has something to do with the fact that isArchived is an attribute of the abstract entity. Is there something special I need to do to filter with inherited attributes?
Thanks!
Maybe hard-coding the value does not work. I recommend using the standard syntax:
[NSPredicate predicateWithFormat:
#"(licenseType like %#) && (isArchived = %#)", lType, #(NO)];
If this does not work I would manually check the value of your isArchived property via sqlite3 command line tool or SQLite Manager plugin in Firefox. Maybe you are expecting a value there that has not been persisted.
If all these are verified it could be that you are reading the wrong values or displaying the wrong values although the predicate actually works.
I'd rather prefer better the blocks instead of the format, hence I would try something like this:
[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
return ([[evaluatedObject licenseType] rangeOfString:lType].location == NSNotFound && [[evaluatedObject isArchived] boolValue] == NO);
}];
The issue turns out to be one of how the predicate tests the conditional versus a direct test via an if statement. My seed data created the license via direct setter calls and I was not explicitly setting isArchived to NO. Within the app, when a license was created it was being set. So, seed data was failing to be returned via the predicate. However, if I manually looped through the set and did a test via "[license isArchived] == NO" both the seed data and the app data were returned as expected.
My guess is that a direct boolean test checks for a valid 'YES' and if not there assumes NO whereas the predicate is explicitly checking YES=1 NO=0 (or however it is represented).

Modelling short-term objects in Core Data app

I have an app that talks to the server to get some items (Item class) for current user and store it. So far so good.
I want to implement search, that essentially returns me a set of Item objects, but obviously I do not want to persist every search result there ever be. Another use case is that server API has different endpoints like recommendations/ new/ upcoming/ that return the same Item object, but in different context, so I would like to differentiate between them somehow.
My first thought was to use a throw-away managed context, load objects from API in there, do fetch and when user is done just destroy the context. Is it a good idea in general? It saves code, because most of my VCs already talk to core data.
Rather than throwing the whole wonderful infrastructure of Core Data away, you should leverage it to achieve your purpose.
Add a timestamp attribute to your entity and use it to selectively display search results or even purge your store from old items.
Add a category attribute to your entity and filter by category when searching.
Both can be achieved with an NSPredicate that you add to your NSFetchRequest. For example:
fetchRequest.predicate = [NSPredicate predicateWithFormat:
#"timestamp > %#", [[NSDate date] dateByAddingTimeInterval:numberOfSeconds]];
or
fetchRequest.predicate = [NSPredicate predicateWithFormat:
#"category = %#", #"new"];

Core Data: object in a predicate

In my core data object model I have 3 entities with appropriate relationships so that MyObject can have many MyObjectProperties, and each property can have one MyObjectPropertyImage.
Given a myObject I want to fetch all the images.
I try to do it using the following predicate, however I get an empty array:
[NSEntityDescription entityForName:#"MyObjectPropertyImage" inManagedObjectContext:managedObjectContext];
[NSPredicate predicateWithFormat:#"ANY myObjectProperty.myObject == %#", myObject];
Any ideas?
When working with Core Data it's best to think of your entities as just that: entities in an object graph, instead of tables in a database. Therefore, you don't need to fetch entities related to others using a predicate. Instead, navigate the object graph using the relationships defined in the model. To get all the images related to myObject:
// assuming the relationships are called 'myObjectProperties' and 'myObjectPropertyImage', respectively
NSSet *allImages = [myObject.myObjectProperties valueForKey:#"myObjectPropertyImage"];
Note that this may trigger additional trips to the database if your object graph is not loaded in memory for your myObject entity. To avoid that, make sure you set the pre-fetching relationship keypaths in your fetch request for myObject.
I hope this helps...
Since you have a MyObject instance in hand and it has the relationship path of myObjectProperties-->ObjectProperty-->>PropertyImages you just need to traverse the relationships. It's easy to do this with valueForKeyPath:
Thusly:
NSArray *images=[myObjectInstances valueForKeyPath:#"myObjectProperties.propertyImage"];
(Note: I might have your attribute names wrong but you can get the idea.)
As general rule, you never fetch when have an object from the graph available. You fetch to "pick out thread" of objects matching the predicate and then to find all related objects you follow the thread/relationships to the related objects.

Resources