iOS fetchRequest using sort descriptor on modified keys - ios

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?

Related

CoreData overwriting Getter and Predicates

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

Multiple KeyMappers per JSONModel

JSONModel lets you convert a model object into a NSDictionary as following:
NSDictionary *dict = [myJSONModel toDictionary]
It includes all properties of the model (except optional). However, I also need to create multiple dictionaries having only some model fields required for a particular backend operation.
Some fields could be included in multiple dictionaries, so ideally, it would be awesome if I could do something like:
NSDictionary *dictOne = [myJSONModel dictionaryWithKeyMapper:myJSONMapperOne]
NSDictionary *dictTwo = [myJSONModel dictionaryWithKeyMapper:myJSONMapperTwo]
and it only returns the objects that have been mapped in that particular mapper.
I am sure there's nothing like this at present. The keymapper for every class is created only once and then is cached, so you can't changed it programatically. Plus you cannot ignore properties via the keymapper.
JSONModel is built like so that it supposes you always need to do the same transformations when you convert between JSON and your model, this way it can do the performance optimisations it does.
However "toDictionary" is not too complicated, you could try subclassing JSONModel and playing around with implementing a "toDictionaryWithFieldList" that takes in field names list and exports only those fields ... just an idea

CoreData (MagicalRecord) returning the wrong values

To save the primary animal in a zoo I do this:
-(void)makePrimary:(Animal*)animal
Zoo *currentZoo = [Zoo findFirstByAttribute:#"zooId" withValue:self.currentZooId];
currentZoo.primaryAnimalId = animal.animalId;
[[NSManagedObjectContext defaultContext] saveToPersistentStoreAndWait];
DLog(#"primaryAnimalId: %d", currentZoo.primaryAnimalId.intValue); //logs "3"
}
After doing this, the primaryAnimalId for currentZoo logs as 3. I am using the Firefox SQLite Manager to verify this. ZPRIMARYANIMALID is 3 there as well.
Now, I navigate to a different section of the app, where I need to display the primary animal. So I do this:
-(Animal*)getPrimaryAnimalForCurrentZoo
{
Zoo *currentZoo = [Zoo findFirstByAttribute:#"zooId" withValue:self.currentZooId];
DLog(#"primaryAnimalId: %d", currentZoo.primaryAnimalId.intValue); //logs 2
Animal *primaryAnimal = [Animal MR_findFirstByAttribute:#"animalId" withValue:currentZoo.primaryAnimalId];
return primaryAnimal;
}
Much to my chagrin, I get "2" for the primaryAnimalId, and thus the wrong animal is returned. "2" is the previous value of the animal before I change it via makePrimary:. currentZooId is 0 in both methods.
What I don't understand is how I can get the wrong value back from Core Data when I can clearly see the correct value currently in the database (in SQLite Manager). How is this possible and how do I fix it?
Did you update self.currentZooId to reflect your changes?
Update:
So, you're asking for the first record in your data set here. The thing is,you're not specifying an order. That is, findFirst just asks the data store for all, and then just returns the first in the list (have a look at the code for more specifics). Since that list can be in any order, you're going to get whatever the data store gives you in at particular instance. What you want to do is add a sort parameter to findFirst to make sure you always get the item you expect. Look at the headers to find the correct firstFirst method with a sort parameter.

Is it posible to conditionally disable NSValueTransformer for NSManagedObject attribute?

Specifically, say I have an NSManagedObject with a "statusCode" attribute set to transformable, and a reversible value transformer subclass to covert from NSStrings to NSNumbers and vice versa. The idea is to use the value transformer so that I receive JSON and a string from a "status" key in the JSON automatically maps to an NSNumber that represents that status code in an NSManagedObject. Conversely if I were to upload the NSManagedObject to a server, at that point its status attribute would be transformed from an NSNumber to a string for the JSON.
So far so good. But, what if I also want to be able to get a simple int out of the NSManagedObjec's status property, so that I can AND it with enums in code?
That is, I'd lie to cover 3 cases:
myManagedObject.status = [JSONResponse valueForKey:#"status"] (should use transformer to do NSString -> NSNumber)
[JSONforUpload setValue:myManagedObject.status forKey:#"status"] (should use transformer to do NSNumber->NSString)
From elsewhere in code, anything along the lines of: if(myManagedObject.status & statusInProgress) ... where statusInProgress is an enum.
I'm thinking I could temporarily disable the value transformer, however I have no idea if the NSManagedObject has a reference to it, or if I should disable it from the NSValueTransformer class, which apparently keeps a table of registered transformers?
I know that for the 3rd case I could just do [myManagedObject.status intValue] and then do the bitwise comparison, but I'm wondering if there's any way I can have the intValue] be returned automagically, from the user of this object's point of view.
Any ideas?
Why don't you just write two additional methods for the JSON transform and leave the property as integer? Then you'd have the best from both worlds.
One approach would be to add a property to the transformer so that it switches between string and enum reversed values. That would work, though I ended up doing a enum<->string transformer and not using it over a transformable attribute (instead I left the managed object's attribute as int) but rather instantiating it only for the JSON <-> object conversion. After that, throughout code I just use the int attribute as is.
Assuming that this entity has its own distinct managed object subclass, you could also simply add another pair of accessor methods to the class to encapsulate the conversion between NSNumber and int values. (Or add a transient attribute, if it needs to be part of the model. But you'd still need to write custom accessors to synch up the values.)

Does using a class and dot notation access in Core Data get a mutable set?

I have an entity called LogBook which has an attribute (called columns) for a set of LogBookColumn entities (one-to-many relationship).
The standard way I see to retrieve the mutable set of columns seems to be:
NSEntityDescription *myLogbook;
myLogbook = [NSEntityDescription insertNewObjectForEntityForName:#"LogBook"
inManagedObjectContext:self.managedObjectContext];
NSMutableSet *columns = [myLogbook mutableSetValueForKey:#"columns"];
Instead of method on the third line, I want to use dot notation. To do so, I have created class definitions, also called LogBook and LogBookColumn, and use #property to create the setters and getters.
LogBook *myLogBook;
myLogbook = [NSEntityDescription insertNewObjectForEntityForName:#"LogBook"
inManagedObjectContext:self.managedObjectContext];
NSMutableSet *columns = (NSMutableSet *)myLogbook.columns;
So, is columns truly a mutable set by default? I have done two things to verify:
Attempted to write to the list, eg: [columns addObject:aColumn];
Asked with: BOOL isKindOfMutableSet = [myLogbook.columns isKindOfClass:[NSMutableSet class]];
Both work with expected results, which may make this question overkill, but I am very concerned about memory errors that will be difficult to track down. I also wonder if asking the question isKindOfClass will work as I have defined this as a mutable set - so won't it work even if the underlying memory organization doesn't support mutable sets?
All of the above sums up to: is this the right way to access and change the columns property/attribute?
According to this documentation, to-many relationships should be declared as NSSet, which makes sense. Even if the attribute returns an NSMutableSet, it is not guaranteed that updating the NSMutableSet will properly update relationships (which mutableSetValueForKey: does).
If you really want a mutable accessor, then just create a readonly property that wraps mutableSetValueForKey:
Update:
Otherwise, we are supposed to use - (void)addLogBookColumnsObject:(LogBookColumn *)value;, - (void)removeLogBookColumnsObject:(LogBookColumn *)value;, - (void)addLogBookColumns:(NSSet *)values;, - (void)removeLogBookColumns:(NSSet *)values; that were generated by Core Data for us.

Resources