Show NSDecimalNumber as currency in IB - objective-c

I have a core data entity with a property amount, which is a NSDecimalNumber. For this property the entity's class has a method defined with an NSDecimalNumber as argument.
In Interface Builder I bound a table column to this property (using an NSArrayController) and on the column's cell I put an NSNumberFormatter. The formatter is configured in IB as 'currency'.
Now, when I try to enter a number, I get the following error:
-[NSCFNumber decimalNumberBySubtracting:]: unrecognized selector sent to instance 0x1001d5590
Apparently my setter method is receiving a regular NSNumber rather than an NSDecimalNumber. Can I configure my formatter differently, perhaps in code rather than IB, or is the only option to add an additional setter with an NSNumber as argument?

Core Data doesn't store NSDecimalNumber, only NSNumber. NSNumber doesn't have the method that gets called.
You either need to change the entities definition to use NSNumber or build your own NSValueTransformer to store the NSDecimalNumber in Core Data.
Please look here for more details about properties: http://developer.apple.com/library/ios/ipad/#documentation/Cocoa/Conceptual/CoreData/Articles/cdMOM.html%23//apple_ref/doc/uid/TP40002328-SW1

It's not clear to me exactly where your issue is. Either the formatter is giving you a straight NSNumber instead of a NSDecimalNumber or the core data is.
To make the NSNumberFormatter give you NSDecimalNumbers, use the method -setGeneratesDecimalNumbers:
To make the data store give you NSDecimalNumbers, make sure the relevant attribute in the model is set to "decimal number".

Related

Setting date in core data with KVC

This has me a little mystified.
For some reason I can set a date via a property, but trying to set it the same way using KVC causes an error.
I'm trying to make my code more generic, so I'd really like to be able to set my dates via KVC, given my NSManagedObject definitions as they are below.
This isn't my project, and it's a very large project, so I need to work within what's already there.
I read this, but it doesn't quite address this particular issue...
Why is an NSDate in a Core Data managed Object converted to NSTimeInterval?
In the core data modeler...
NSManagedObject SomeClass
updateDate Date
In code...
NSManagedObject SomeClass
#property (nonatomic) NSTimeInterval updateDate
So in a method, I've seen code that does this when setting through the property, which works just fine, but isn't generic.
someClass.updateDate = [NSDate date] timeIntervalSinceReferenceDate
I can't do that through KVC becaus setValue: expects an ID, so it fails with this compile time error 'Sending NSTimeInterval to parameter of incompatible type 'id_Nullable'' when the same is done through KVC
[someClass setValue:[NSDate date].timeIntervalSinceReferenceDate forKey:#"updateDate"];
Suggestions for fixing this so I can use KVC to set my dates.
Thanks a lot
If you pass an NSNumber to setValue:forKey:, it is automatically unboxed if the property is a primitive type. So you should be able to do [someClass setValue:#([[NSDate date] timeIntervalSince1970]) forKey:#"updateDate"].

Picking an item of NSArray using key value coding

If you read listing 4 in the Animation section Apple's Core Animation guide, it seems to use KVC and the key path "filters.pulseFilter.inputIntensity" to pick out an object called "pulseFilter" out of an NSArray. "pulseFilter" is actually a CIFilter named "pulseFilter" by calling the method setName.
Now, I don't see the method setName defined anywhere. I also don't believe you can select a specific item out of an NSArray by using a key. Can someone explain how this works?
It does not necessarily have to go through standard valueForKey: or valueForKeyPath: of the NSArray.
Objects are free to provide own valueForKeyPath: method and handle KVC its own way and this is what probably the class of this selectionLayer object does.
To retrieve an object from NSArray using a property name + property value.
Step 1: get your stored object compliant to KVC (NSKeyValueCoding protocol) by implementing valueForKey: in your custom class.
Step 2: filter your NSArray using NSPredicate (filteredArrayUsingPredicate on your NSArray)
Do you need more detail ?

Convert primitive Datatypes to NSNumber

We are trying to write a Wrapper from NSObject (using primitive datatypes) to a NSManagedObject (using NSNumber).
So we have to automatically convert the primitive type to a NSNumber.
We know the objCType through NSInvocation ([invocation.methodSignature getArgumentTypeAtIndex:2]) and also get the value to set ([invocation getArgument:&argument atIndex:2]) this way.
So the problem is: How to use this information to create a NSNumber out of it.
Any ideas?
There are several approaches to do this. They're all outlined under Non-Standard Persistent Attributes in the Core Data documentation.
Core Data will then convert your data on the fly to / from what you need.

Incompatible Type for Argument 1 Objective-C

I have a function that sets an entity within a Core Data store. I used to have all values it would be storing as type double, however now I must make it accommodate NSStrings as well. Consequently, I changed the type of the parameter the function takes in, to an id type. However, now I get the error:
error: incompatible type for argument 1 of 'numberWithDouble:'
...at the following lines:
//...
[dfm setTimeStamp:[NSNumber numberWithDouble:value]];
//...
[[fetchedObjects objectAtIndex:0] setValue:[NSNumber numberWithDouble:value] forKey:#"timeStamp"];
//...
Apparently it doesn't like the [NSNumber numberWithDouble:value] segment of each line. I was contemplating making a container class that holds an NSNumber type (doesn't Apple already have a class like this?) to get around this problem, but I thought that there has to be an easier way I am not thinking of (besides duplicating the function and changing the type of the value parameter). Any ideas? Thanks in advance!
EDIT:
Here is the function declaration:
-(void)setItemInDFMWhilePreservingEntityUniquenessForItem:(attribute)attr withValue:(id)value
attribute is merely an enum which specifies which entity to store within. The problem is that the compiler is giving me problems with value being of type id, theoretically I can pass in anything I want, and I believe the way I have it I am implying that I will be passing it as an NSNumber, but the compiler doesn't like that as that is not actually a class instance I suppose?
The problem is that the compiler is
giving me problems with value being of
type id, theoretically I can pass in
anything I want, and I believe the way
I have it I am implying that I will be
passing it as an NSNumber, but the
compiler doesn't like that as that is
not actually a class instance I
suppose?
By declaring value as id, you can pass any object you want. But why do you "suppose" that NSNumber isn't an object, when it's clearly documented as being an object? The warning isn't about passing an NSNumber instance when you've declared value as an id - that's perfectly valid, because id means "any object," and an NSNumber instance is an object. The warning comes from calling +numberWithDouble:, a method that takes a double for its first argument, and passing it value, which is declared as id - i.e. an object. You can't pass an object to a method that expects a double.
Your proposed solution, typecasting value with (NSInteger)value will silence the warning, but it won't fix the problem. The typecast simply converts the memory address the object pointer targets to an integer value. If (as your edit suggests) value is already an NSNumber object, what do you hope to gain by creating another one, or by typecasting its memory address to an integer? Just do:
[dfm setTimeStamp:value];
The problem lies with the value variable. It should be declared as a double (primitive) for this call to succeed.
edit: after rereading your question, do a check in the function on the type of value, if it is an NSString (use [value isKindOfClass:[NSString class]]) store it as such, if its not then its a double (if thats the only two types you are passing) and store it as such.
Can't you just pass the NSNumber instead of double?
Just realized that the call I was making (numberWithDouble:) was having the compiler check for a primitive, i.e. double. Changing it to the following worked like a charm:
[dfm setTimeStamp:[NSNumber numberWithInteger:(NSInteger)value]];
Thanks to those that responded!

How to test property existence and type based on NSString typed key?

In my quest to update a Core Data model within my iOS project, I'm querying a server for JSON objects that correspond - to some extent - with the managed entities of my model. The end result I'm striving for is a reliable update solution from JSON output.
For the examples in this question, I'll name the core data managed object existingObj and the incoming JSON deserialized dictionary updateDict. The tricky part is dealing with these facts:
Not all properties of the existingObj are present in the updateDict
Not all properties of the updateDict are available in the extistingObj.
Not all types of existingObj's properties match the JSON deserialized properties. (some strings may need a custom Objective-C wrapper).
updateDict may contain values for keys that are uninitialized (nil) in existingObj.
This means that while iterating through the updated dictionaries, there has to be some testing of properties back and forth. First I have to test whether the properties of the updateDict exist in existingObj, then I set the value using KVC, like so:
// key is an NSString, e.g. #"displayName"
if ([existingObj respondsToSelector:NSSelectorFromString(key)) {
[existingObj setValue:[updateDict objectForKey:key] forKey:key];
}
Although this part works, I don't like the fact that I'm actually testing for displayName as a getter, while I'm about to call the setDisplayName: setter (indirectly via KVC). What I'd rather to is something like [existingObj hasWritablePropertyWithName:key], but something that does this I can't find.
This makes for subquestion A: How does one test for a property setter, if you only have the property's name?
The next part is where I'd like to automate the property identification based on their types. If both the updateDict and the existingObj have an NSString for key #"displayName", setting the new value is easy. However, if the updateDict contains an NSString for key #"color" that is #"niceShadeOfGreen", I'd like to transform this into the right UIColor instance. But how do I test the type of the receiving property in existingObj so I know when to convert values and when to simply assign? I was hoping for something along the lines of typeOfSelector:
if ([existingObj typeOfSelector:sel] == [[updateDict objectForKey:key] class]) {
// regular assignment
} else {
// perform custom assignment
}
Of course this is boguscode. I can't rely on testing the type of the existingObj-property's value, for it may be unitialized or nil.
Subquestion B: How does one test for the type of a property, if you only have the property's name?
I guess that's it. I figured this must be a dupe of something that's already on here, but I couldn't find it. Maybe you guys can?
Cheers, EP.
P.S. If you'd have a better way to synchronize custom Objective-C objects to deserialized JSON objects, please do share! In the end, the result is what counts.
If you want to query whether an object has a setter for a given KVC key called key which corresponds to a declared property, you need to check whether it responds to a selector method called setKey: (starts with set, capitalise the first character in key, add a trailing colon). For instance,
NSString *key = #"displayName";
NSString *setterStr = [NSString stringWithFormat:#"set%#%#:",
[[key substringToIndex:1] capitalizedString],
[key substringFromIndex:1]];
if ([obj respondsToSelector:NSSelectorFromString(setterStr)]) {
NSLog(#"found the setter!");
[obj setValue:someValue forKey:key];
}
Two remarks:
Even though properties can have setters with names that do not follow the pattern described above, they wouldn’t be KVC compliant, so it is safe to check for set<Key>: since you’re using KVC to set the corresponding value.
KVC doesn’t use the setter method only. If it doesn’t find a setter method, it checks whether the class allows direct access to instance variables and, if so, use the instance variable to set the value. Also, if no setter method or instance variable is found, it sends -setValue:forUndefinedKey: to the receiver, whose class might have overridden the standard implementation that throws an exception. This is described in the Key-Value Coding Programming Guide.That said, if you’re always using properties, checking for the setter method should be safe.
As for your second question, it is not possible to query the runtime to know the actual Objective-C class of a property. From the runtime perspective, there’s an implementation specific type encoding for properties and general types (such as method parameters/return types). This type encoding uses a single encoding (namely #) for any Objective-C object, so the type encoding of an NSString property is the same as the type encoding of a UIColor property since they’re both Objective-C classes.
If you do need this functionality, one alternative is to process your classes and add a class method that returns a dictionary with keys and corresponding types for every property (or the ones you’re interested in) declared in that class and superclasses, or maybe some sort of description language. You’d have to do this on your own and rely on information not available during runtime.