Is it posible to conditionally disable NSValueTransformer for NSManagedObject attribute? - ios

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.)

Related

NSCoding to CKRecord

I have a custom class with which i made an (swift-y) struct NSCodable. Now I want to convert that into a CKRecord in order to use CloudKit. Even though I set 'key value'-pairs when encoding my struct, it is in my understanding that the struct is converted into NSData and that you can't convert it to a Dictionary (or another key-value object). So I get the feeling that this is not the way to go.
Is there a way to make this conversion directly? Or with a step in-between (for instance converting the Data into a [String: String]- dictionary)?
NSCoding is only for going from/to the same class. Instead, write a toServerDictionary method on your custom class, including only the properties you want to send to CloudKit, and then use the result to call setValuesForKeys on a CKRecord.
You'll likely find there are properties that need to be specific types, in which case its better to make your method toServerRecord and create a CKRecord and return it. You can also have a updateWithServerRecord to set anything you receive back.

What is the content of character attributes like NSFontAttributeName?

I was reading the apple documentation to learn how to format text for iOS. I came across an attributed string and most of it made sense to me. However, while looking up the different kinds of attributes, I saw that they are all declared as NSString.
For example:
NSString *const NSFontAttributeName;
NSString *const NSParagraphStyleAttributeName;
...
...
We pass in these string objects in a dictionary with the value being that particular attribute (e.g., a UIFont object). However, what I do not understand is what the content of that string has to do with the attribute itself. Do they just contain the name of the attribute? (i.e NSFontAttributeName might contain a string like #"NSFontAttribute")
Surely there must be a reason why has apple to chosen to do it this way?
Edit: My question isn't about why they use a string object as a key to the dictionary but why they use a predefined constant string object named NSFontAttributeName instead of allowing us to manually pass in a string #"NSFontAttribute" as the key. That's why I wondered whether the contents of their predefined string object has anything to do with this.
In this case, Apple uses an NSDictionary to contain an arbitrary set of key/value pairs. Doing it this way means you have a lot of flexibility because you can have no attributes, one attribute, or two dozen attributes with the same programming interface. And if in two years time there are attributes available that you haven't even thought about, Apple doesn't have to introduce any new APIs to support you setting these attributes.
The reason for using a constant instead of a string literal is that the compiler can save you if you misspell a name. If you wrote #"NSFontattribute" instead of #"NSFontAttribute", the compiler wouldn't know that you got it wrong.
As the attributes are expressed in an NSDictionary and you can only hold Objective-C objects in Objective-C collection classes, you have to use an object of some sort. They could have used enumerated integer values, wrapped in NSNumber objects, instead:
typedef enum {
KEYONE,
KEYTWO
} KeyValues;
NSDictionary *attributes = [NSDictionary dictionary];
attributes[#(KEYONE)] = #"The attribute";
One advantage of using an NSString as a key is that it's easier to debug at the slight expense of generating the dictionary key hash from the string (which must be very slightly more expensive than generating it from an NSNumber object).

NSMutableDictionary contents inconsistent with output of allValues

So long story short, there's a discrepancy between the output of a NSMutableDictionary's contents and the result of calling allValues on the same object. Below is some debugger output after inspecting the object which demonstrates my problem: (made generic of course)
(lldb) po self.someDict.allKeys
<__NSArrayI 0xa5a2e00>(
<SomeObject: 0xa5a2dc0>,
<SomeObject: 0xa5a2de0>
)
(lldb) po self.someDict.allValues
<__NSArrayI 0xa895ca0>(
0.5,
0.5
)
(lldb) po self.someDict
{
"<SomeObject: 0xa5a2dc0>" = (null);
"<SomeObject: 0xa5a2de0>" = (null);
}
So as we can see, the actual output of the NSMutableDictionary contains null values for both its entries, but the contents of .allValues contains the proper data. These three outputs were taken at the same time in execution.
I'm not sure why this is happening, but I think it may have something to do with the fact that I'm encoding/decoding the object which this dictionary is a property of using CoreData. I believe I'm doing this properly:
[aCoder encodeObject:self.someDict forKey:#"someDict"];
and to decode
self.someDict = [aDecoder decodeObjectForKey:#"someDict"];
The weird thing is that if I inspect the dictionary before it ever gets encoded, it is still in the state described at the beginning of the post, so this is why I'm doubtful the CoreData actions are screwing with the contents.
As always, please don't hesitate to request additional information.
EDIT: The problem was as answered below. I was using a custom class which didn't cooperate with isEqual, so my solution was to change the storage and structure of my logic, which made using a Dictionary unnecessary.
I have not been able to duplicate the problem using NSString as keys and NSNumber as values. I suspect that your custom class does not properly implement hash and/or isEqual. Specifically, the results from those two methods must be consistent, meaning that if isEqual returns true, then the hash values for the two objects must be identical.
You also need to ensure that your class implements NSCopying properly and that a copy is equal to the original.
As a general rule, don't use custom objects for dictionary keys. Just use strings and be done with it.
As user3386109 points out, custom objects must properly implement the -hash and -isEqual methods in order to be used as dictionary keys, and even then, custom objects don't work correctly for dictionary keys for things like key/value coding.

Understanding Transient properties with NSFetchedResultsController

I'm starting to create an application with Core Data, to retrieve a data for sectioned table i want to use NSFetchedResultController, in the example from apple there are two additional properties.
primitiveTimeStamp
primitiveSectionIdentifier
For the case of primitiveSectionIdentifier apple says that
In contrast, with transient properties you specify two attributes and
you have to write code to perform the conversion.
because the sectionidentifier is transient property.
But what about the timeStamp ?this attribute is not a transient, why there is a primitiveTimeStamp property ? and why there is explicit setter for timeStamp ?
- (void)setTimeStamp:(NSDate *)newDate {
// If the time stamp changes, the section identifier become invalid.
[self willChangeValueForKey:#"timeStamp"];
[self setPrimitiveTimeStamp:newDate];
[self didChangeValueForKey:#"timeStamp"];
[self setPrimitiveSectionIdentifier:nil];
}
or maybe it's not a actual setter? where is _timeStamp=newDate?
CoreData generates the accessors for you. It generates "public and primitive get and set accessor methods for modeled properties".
So in this case it has generated:
-(NSDate*)timeStamp;
-(void)setTimeStamp:;
-(NSDate*)primitiveTimeStamp;
-(void)setPrimitiveTimeStamp:;
"why there is a primitiveTimeStamp property ?"
The declaration is merely to suppress compiler warnings. ie. If you removed the declaration of the property you'd find a warning on compilation but the code would still run.
Or alternatively you could use [self setPrimitiveValue:newDate forKey:#"timeStamp"];
"why there is explicit setter for timeStamp ?"
This is required since setting the timeStamp requires the 'sectionIdentifier' to be recalculated. This is achieved by setting it no nil and letting the get accessor recalculate it lazily.
"where is _timeStamp=newDate?"
The equivalent of this is essentially done in the auto generated implementation of setPrimitiveTimeStamp.
A quote from the docs:
By default, Core Data dynamically creates efficient public and primitive get and set accessor methods for modeled properties (attributes and relationships) of managed object classes. This includes the key-value coding mutable proxy methods such as addObject: and removes:, as detailed in the documentation for mutableSetValueForKey:—managed objects are effectively mutable proxies for all their to-many relationships.
Note: If you choose to implement your own accessors, the dynamically-generated methods never replace your own code.
For example, given an entity with an attribute firstName, Core Data automatically generates firstName, setFirstName:, primitiveFirstName, and setPrimitiveFirstName:. Core Data does this even for entities represented by NSManagedObject. To suppress compiler warnings when you invoke these methods, you should use the Objective-C 2.0 declared properties feature, as described in “Declaration.”

Why isnt BOOLEAN attribute of a Core Data entity__NSCFNumber and not __NSCFBoolean?

In Core Data Entities, we have to wrap primitive numbers (char,int,float,double and BOOL) to there Class cluster NSNumber.
Although the core data user interface let's us specify a type where I specifically mentio it as BOOLEAN and default type to YES or NO.
Now when I get a JSON-converted-NSDictionary from an API call I try to store a boolean true or false into this attribute.
However I have noticed, while printing the class of both the core data entity and the json-dictionary's corresponding value for the key in the debugger console, that
Core Data entity's NSNumber wrapped attributes class is __NSCFNumber and for the dictionary's value for the key is __NSCFBoolean.
I know about the class cluster pattern and I understand that we have subclasses under NSNumber whose actual objects are created when numberWithInt or numberWithBool are called and are represented as the one umbrella term NSNumber.
But what I did not understand was, why didnt the debugger show Core Data entities bool attribute as __NSCFBoolean ?

Resources