Question

I have a JSON service that registers a new user. When there's an error like "E-mail in use" or "Taken user name" etc., it returns error codes seperated by "|" character (string). When the registration is successful, it returns the registered user's ID (integer).

For example, a registered e-mail and user-name (both in one case) error would return:

{"error" = "200|300|";}

And a successful registration would return:

{"error" = 1234;}

So I split the error by "|" character into an array, then show error(s) like this:

NSArray *errorCodes = [[jsonData objectForKey:@"error"] componentsSeparatedByString:@"|"];

When there is error/are errors, this line works great. But when there is no error, this line of code crash as following:

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFNumber componentsSeparatedByString:]: unrecognized selector sent to instance 0x7579fc0'

If I'm not mistaken, it crashes because it tries to split a string which isn't actually a string, but a number.

So I tried to work around this by checking the type of object class like:

if ([[jsonData objectForKey:@"error"] isKindOfClass:[NSNumber class]]) {
    // success
} else if ([[jsonData objectForKey:@"error"] isKindOfClass:[NSString class]])
    // show errors
}

However, it didn't get into none of these statemens. When I tried to check like [__NSCFNumber class] instead of [NSNumber class], it returned error:

Unknown receiver '__NSCFNumber'; did you mean 'NSNumber'?

Same for [NSString class].

How can I work this out by checking the object type? I know I can use other ways but this one seems like the proper way since I might be needing this control in the future for different data types.

Était-ce utile?

La solution

As __NSCFNumber is a (private) sub-class of NSNumber, checking for NSNumber is the correct check.

As __NSCFNumber is private, you should not depend on it. Apples engineers could decide to change the name, return another class, whatever.

If you insists of checking for __NSCFNumber, you can use NSClassFromString()

if([obj isKindOfClass:NSClassFromString(@"__NSCFNumber")])

But I wouldn't do that.

Autres conseils

NSCFNumber is a private subclass of NSNumber, but that is an implementation detail which you don't have to worry about.

About the response you this is not good way to retrun id in error object. In this case error should be nil or should not present at all, and you should have another parameter with id which should have id value.

Instead of checking the class of error object, you can check if the object responds to selector:

    if ([[response objectForKey:@"error"] respondsToSelector:@selector(componentsSeparatedByString:)]) {
        // show errors
    } else {
        //success
    }

This approach is sometimes called Duck typing.

Testing with isKindOfClass: is the correct approach. I've verified this with the following code snippet:

    NSString* json = @"{ \"error\" : 1234, \"error_str\" : \"testing\" }";
    NSDictionary* d = [NSJSONSerialization JSONObjectWithData:[json dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil];

    NSLog(@"error: %d (class %@), error_str: %d (class %@)",
          [d[@"error"] isKindOfClass:[NSNumber class]], [d[@"error"] class],
          [d[@"error_str"] isKindOfClass:[NSNumber class]], [d[@"error_str"] class]);

which gives the following output:

2014-01-19 12:43:07.236 Test[64222:303] error: 1 (class __NSCFNumber), error_str: 0 (class __NSCFString)

I couple of questions:

  1. Have you verified [jsonData objectForKey:@"error"] is returning a non-nil value when the isKindOfClass: check fails? Because if it is nil, isKindOfClass: will return 0.
  2. What JSON parser are you using? NSJSONSerialization or a 3rd party parser?
  3. Is the JSON you listed above really what you are parsing? Because they are not valid JSON. For example, {"error" = "200|300|";} uses an equals sign (should be colon) and a semicolon (should be comma or nothing, since it's the last element in the dictionary).
id error = jsonData [@"error"]; // Could produce anything
if (error == nil)
{
    // There wasn't actually any "error" in your json data
}
else if (error == [NSNull null])
{
    // json contained "error": null so you were told there is no error information
}
else if ([error isKindOfClass:[NSNumber class]])
{
    NSNumber* errorAsNumber = error;
    // json contained "error": 1234 or something similar
}
else if ([error isKindOfClass:[NSString class]])
{
    NSString* errorAsString = error;
    // json contained "error": "some string"
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top