KVC string conversion not working for BOOL value - objective-c

Hey. I am reading in a string from a file and attempting to use the resulting string to set a BOOL property on an object using the KVC method -setValue:forKeyPath:. However, this breaks with an exception: -[NSCFString charValue]: unrecognized selector sent to instance 0x7fff711023b0. I'm guessing this is because BOOL is typedef'd from char. Is there a way around this? Thanks!

When setting a BOOL property using KVC, you need to pass an NSNumber object. What you could do in your case is pass [NSNumber numberWithBool:[myString boolValue]]. That should fix your crash.

I am catching the exception, checking it's name, and then retrying with a wrapped value when needed. Here is the code:
#try
{
[(NSObject*)retObj setValue:[[obj keyValuePairs] objectForKey:key]
forKeyPath:key];
}
#catch (NSException * e)
{
if ([[e name] isEqualToString:NSInvalidArgumentException])
{
NSNumber* boolVal = [NSNumber numberWithBool:[[[obj keyValuePairs] objectForKey:key] boolValue]];
[(NSObject*)retObj setValue:boolVal
forKeyPath:key];
}
}
Thanks anyway!

Add a simple category to your project:
#implementation NSString (CharValue)
- (BOOL)charValue {
return [self isEqualToString:#"0"] ? NO : YES;
}
#end

Related

how to test object equality using xcttest?

My method returns a NSNumber* and I want to unit test this method. Since my actual return value is NSNumber*, I create a new expected value of NSNumber*, but it fails. Here is the code:
NSNumber *cRating = [movie getRating:ratingDictionary ratingType:criticRating];
XCTAssertEqualObjects(cRating, [[NSNumber alloc]initWithInt:70], #"");
The error is:
[SFModelTest testGetCriticRatingMethod] failed: ((cRating) equal to ([[NSNumber alloc]initWithInt:70])) failed: ("70") is not equal to ("70")
Since it is saying "70" is not equal to "70", I am guessing it has to do with alloc init. Some pointer stuff that is not equal. Can somebody please help? Thank you.
Edit for comment: adding getRating method
- (NSNumber *)getRating:(NSDictionary *)movieDic ratingType:(enum RatingsEnum) rating{
NSNumber *result = 0;
NSNumber *ratingNum = 0;
switch (rating) {
case userRating:
{
ratingNum = [movieDic objectForKey:#"audience_score"];
break;
}
case criticRating:
{
ratingNum = [movieDic objectForKey:#"critics_score"];
break;
}
default:
break;
}
if(ratingNum && ratingNum > 0)
{
result = ratingNum;
}
return result;
}
The method returns NSNumber. And my test is:
NSNumber *cRating = [movie getRating:ratingDictionary ratingType:criticRating];
XCTAssertEqualObjects(cRating, [[NSNumber alloc]initWithInt:70], #"");
When I do a class NSLOG, it returns __NSCFConstantString. I am confused now.
I think that you're doing something wrong because XCTAssertEqualObjects(#(1), #(1), #"Not equal."); or XCTAssertEqualObjects([NSNumber numberWithInt:1], [NSNumber numberWithInt:1], #"Not equal."); if you are not familiar with literals, will pass.
You should checkout the getRating:ratingType: method to see which type of object it returns.
Try adding a breakpoint just before that XCTAssertEqualObjects and inspect the cRating instance.

Cocoa persist coredata NSNumber from tableview not working

I am unable to save numeric values to coredata via a bound tableview and arrayController. I have changed the code to return hardcoded values, it doesn't crash when i try edit the numeric column but doesn't persist the hardcoded value:
#import "StringToNumberTransformer.h"
#implementation StringToNumberTransformer
+ (Class)transformedValueClass { return [NSNumber class]; }
+ (BOOL)allowsReverseTransformation { return YES; }
- (id)transformedValue:(id)value {
// return (value == nil) ? nil : [NSString stringWithFormat:#"%i", [value shortValue ]];
//return (value == nil) ? nil : NSStringFromClass([value class]);
return #"1";
}
- (id)reverseTransformedValue:(id)value {
// return [value numberFromString:[value stringValue]];
return [NSNumber numberWithInt:2];
}
#end
Here is the link to the xib setup ! http://tinypic.com/r/2nty41s/5 !
Can anyone shed some light on this?
Many thanks
As stated in the docs for Custom Value Transformers (Value Transformer Programming Guide)
A value transformer subclass must implement the transformedValueClass
class method. This method returns the class of the object that the
transformedValue: method returns.
Thus, your transformedValueClass returns the class for NSNumber, but your transformedValue: method returns an instance of type NSString
Since you're converting from an NSString to an NSNumber, I imagine you'll want to swap the implementations of your transformedValue: and reverseTransformedValue: methods.

How to test properties of class using TDD?

Why when I use method respondsToSelector: or instancesRespondToSelector: at line 43 I cannot bypass STAssertTrue?
//My test case code
- (void)testApiClass {
//Check object
NSString* classKey = #"Api";
id obj = NSClassFromString(classKey);
STAssertNotNil(obj, [NSString stringWithFormat:#"Model '%#' not found.", classKey]);
//Check properties
NSArray* properties =
#[
#"performSyncRequestWithUri::",
#"performAsyncRequestWithUri:::",
];
for (NSString* property in properties) {
SEL propertySel = NSSelectorFromString(property);
BOOL isRespondsToSel = [obj respondsToSelector:propertySel];
STAssertTrue(isRespondsToSel, [NSString stringWithFormat:#"Property '%#' not found on object of class name '%#'", property, [obj class]]);
}
}
#interface Api : NSObject
- (NSDictionary*)performSyncRequestWithUri:(NSString *)requestUri params:(NSDictionary *)params;
- (void)performAsyncRequestWithUri:(NSString *)requestUri params:(NSDictionary *)params completionHandler:(void (^)(NSDictionary *, NSError *))completionBlock;
#end
The string constants in your properties array don't match the selectors in your Api interface.
Also, neither of those selectors refers to a property. A property has two selectors: a getter, like stringValue, which has no colons, and a setter, like setStringValue:, which has one colon and (usually) starts with set.
Instead of embedding your selectors in strings, make an array of selectors:
SEL selectors[] = {
#selector(performSyncRequestWithUri:params:),
#selector(performAsyncRequestWithUri:params:completionHandler:),
NULL
};
for (size_t i = 0; selectors[i]; ++i) {
SEL selector = selectors[i];
BOOL respondsToSelector = [obj respondsToSelector:selector];
STAssertTrue(respondsToSelector, [NSString stringWithFormat:
#"Object %# doesn't respond to selector %s",
obj, sel_getName(selector)]);
}
The advantages here are that Xcode will autocomplete the selectors for you, and you can command-click the selectors to jump to their definitions.
The methods are called performAsyncRequestWithUri:params:completionHandler: and performSyncRequestWithUri:params:

NSFormatter for BOOL

I have set up my simple Xcode project with a table that is binded to an array controller. It works fine if the array controller is full of entities with a string attribute. However I want to change the attribute to a BOOL and have the table show the string "true" or "false" based on the BOOL.
I have overrided the following two methods from NSFormatter:
-(NSString*) stringForObjectValue:(id)object {
//what is the object?
NSLog(#"object is: %#", object);
if(![object isKindOfClass: [ NSString class ] ] ) {
return nil;
}
//i'm tired....just output hello in the table!!
NSString *returnStr = [[NSString alloc] initWithFormat:#"hello"];
return returnStr;
}
-(BOOL)getObjectValue: (id*)object forString:string errorDescription:(NSString**)error {
if( object ) {
return YES;
}
return NO;
}
So the table gets populated with "hello" if the attribute is a string however if I switch it to a boolean, then the table gets populated with lots of blank spaces.
I don't know if this helps but on the line where I'm outputting the object, it outputs __NSCFString if the attribute is a string and "Text Cell" if I switch the attribute to a boolean. This is something else I don't understand.
Ok, it's not 100% clear what you're trying to do from the code, but first things first - BOOL is not an object, it's basically 0 or 1, so to place BOOL values into an array, you're probably best off using NSNumber:
NSNumber *boolValue = [NSNumber numberWithBool:YES];
and placing these into your array. Now you want to change your method:
-(NSString*) stringForObjectValue:(id)object {
NSNumber *number = (NSNumber *)object;
if ([number boolValue] == YES)
return #"true";
else
return #"false";
}
There's a few things here - for example, you want to avoid passing around id references if you can (if you know all your objects in the NSArray are NSNumber, you shouldn't need to).

Newbie! my app breaks down in if statement: NSCFString stringValue]: unrecognized selector sent to instance

I am following the Stanford CS193P class and I am trying to do assignment 2. I have now for hours stared at the same error message:
-[NSCFString stringValue]: unrecognized selector sent to instance 0x4b373e0,
while trying endless revisions of the code
The method running is this one:
+ (double)evaluateExpression:(id)anExpression
usingVariableValues:(NSDictionary *)variables;
{
CalculatorBrain *evalBrain =[[CalculatorBrain alloc]init];
for (id element in anExpression) {
if([element isKindOfClass:[NSString class]]) {
NSString *elementFirst=[element substringToIndex:1];
if ([elementFirst isEqual:#"v"])
{
NSString *varLookUp = [element substringFromIndex:0];
[evalBrain setOperand:[[variables objectForKey:varLookUp]doubleValue]];
}
else
{
[evalBrain performOperation:element];
}
}
if ([element isKindOfClass:[NSNumber class]]) {
[evalBrain setOperand: [element doubleValue]];
}
}
return [evalBrain operand];
}
it is at the statement
if ([elementFirst isEqual:#"v"])
the program crashes.
Grateful any hints!
Try
if ([elementFirst isEqualToString:#"v"])
instead.
Here is result of NSLog on elementfirst:
2011-02-06 16:00:11.554 Calculator[14160:207] elementfirst: +
it correctly shows that I hit the '+' key which is not equal to 'v' and the program should go to the else block. Unfortunately it crashes in stead...