I'm trying to localize my Core Data app. I found different approaches and the one I'm interested most in, is described in this question. Basically it creates a new table and overwrites the getter, to return the correct localization.
entity(Book, title, localizedTitle)
entity(LocalizedString, localization, string)
What I don't get right now, is, if it influences the predicate, too. Usually I would write a predicate like this:
[NSPredicate predicateWithFormat:#"title = %#", someString];
In case the property title is in my CoreData Class via a category overwritten to be localized and someString is a user selection I search for. Does this compare the localized title with someString, or title?
So will the predicate use the overwritten property of title and return localizedTitle to compare it with someString, or will it use the empty title field to compare it to someString?
I had to revoke the answer, since I found out, that when you override a getter, the NSPredicate will use the overridden getter. Some weird behavior, but seems like codeData does this.
The NSPredicate itself is not using any of your code if not inserted by you with the %#. It is a SQLite request inside a wrapper object NSPredicate. With title you are not using your title property (so the getter) in one of your wrapper CoraData Objects for a row in the database. title is only an access to a column in your database, nothing transient.
The customized getter you created is for receiving the data from the database through CoreData, packed each row into a wrapper object subclassed from NSManagedObject. If you access a property of this wrapper object it reads the data and localizes it lazily. So nothing from the data has changed.
I tested this with a little app to make sure!
Maybe we mean different things. I have a subclass of NSManaged Object. This subclass uses a custom getter to change the value of the NSManagedObject from #"butter" to #"milch".
The real value in the SQLite database (and only to make sure with #dynamic), the output is 'butter', but with the custom getter every other class I tried will get 'milch' as the attribute from CoreData except NSPredicate. The attribute is called 'name'. Now I use a NSPredicate
NSPredicate *predicate = [NSPredicate predicateWithFormat: #"name = %#", #"milch"];
It is not using the getter. Since all objects would fulfill that predicate with the custom getter:
- (NSString *)name {
return #"milch";
}
Maybe another stackoverflow answer is more precise:
NSPredicate in NSFetchedResultsController doesn't use Category's getter
Related
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).
I have an attribute with an int_32 property called seconds. In a category, I restate the property as readonly and override the getter to create it from other properties. The issue is when I use
[NSPredicate predicateWithFormat:#"SELF.seconds > %i"];
The predicate does not work because the getter is not called by the predicate. It is called in my other code just fine, just not in the predicate. Anyone know why this would happen or how to get around this problem? (besides recreating NSFetchedResultsController).
The predicate is converted into SQL and run on the store. It doesn't run any of your code. As such you can only use the stored value in the data store. No transient attributes, no methods.
If you want to use the attribute in a fetch then you should save the value. This could be done by implementing willSave and having it set the value just before any other value changes are saved.
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).
I have entities with string properties, call it "option" (entity.option is a string). I want to execute a fetch request but I want to compare the float values of "option".
For example, if I have entity1.option = "10" and entity2.option = "5", I want entity2 to be before entity1 after the sort. Sorting by the string values would put entity1 before entity2 since "1" comes before "5".
How can I set up an NSSortDescriptor to do this? I read that compare blocks do not work with fetch requests so can I modify the key used somehow? That is, suppose I create my sortDescriptor via
[NSSortDescriptor sortDescriptorWithKey:ascending:selector:]
Instead of using my property of #"option" as the key, can I do something like #"[option floatValue]"?
EDIT:
I just tried creating a category for NSString that implements a compare method to do what I want. However, even though my code compiles, it fails at run time saying that the compare method I implemented is not a valid selector. I've imported my NSString category into my controller that makes the fetch request but it doesn't work still. I also tried importing the NSString category into my entity that I want to use it but still no luck.
Will this method of using a category not work or am I just not importing the NSString category into the right place?
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.