0

I am getting the following crash on crashlytics.

Fatal Exception: NSUnknownKeyException [<__NSCFString 0x1742aeb80> valueForUndefinedKey:]: this class is not key value coding-compliant for the key url.

This is where the crash occurred for some of the users.

 id url = [[json[@"data"]valueForKey:@"value"]valueForKey:@"url"];

I'm not sure what is the best way to prevent this crash. I believe this is because json[@"data"] is an NSString in certain cases. So I believe I should check if this is an NSDictionary like this.

if ([json[@"data"] isKindOfClass:[NSDictionary class]]) {
     id url = [[json[@"data"]valueForKey:@"value"]valueForKey:@"url"];
    }

Any tips or suggestions are appreciated.

This is my end result after getting answers from here. Does this look okay? I didn't include all my code at first to keep things simple.

        if ([json isKindOfClass:[NSDictionary class]]) {
            id url = nil;
            id type = nil;

            NSDictionary *data = json[@"data"];

            if ([data isKindOfClass:[NSDictionary class]]) {
                type = data[@"type"];
                NSDictionary *value = data[@"value"];

                if ([value isKindOfClass:[NSArray class]]) {
                    url = [value valueForKey:@"url"];
                }

                if ([type isKindOfClass:[NSString class]] && [url isKindOfClass:[NSArray class]] && [url count] != 0) {
                   // do stuff
              }
            }
         }
Curt Rand
  • 1,025
  • 1
  • 9
  • 27
  • Why are you using `valueForKey:` instead of `objectForKey:`? Don't use key-value coding unless you have a clear, specific, and understood reason to do so. – rmaddy Nov 14 '17 at 16:31
  • @trungduc Does the code now look okay to you? I've updated my answer. – Curt Rand Nov 14 '17 at 18:10
  • @CurtRand you should use `id *value` instead of `NSDictionary *value`. It makes more sense. Everything else looks good. – trungduc Nov 14 '17 at 18:15
  • @CurtRand one more, i think you don't need to check `json` is kind of class `NSDictionary`. As i guess, your `json` is a `NSString` so just need to convert `json` from `NSString` to `NSDictonary`. – trungduc Nov 14 '17 at 18:19
  • I have to check if 'json' is an NSDictionary because sometimes the data I get returns as a string and not a dictionary. – Curt Rand Nov 14 '17 at 18:25
  • @CurtRand it’s up to you but you need to make sure there isn’t anything wrong if you check json is a nsdictionary value – trungduc Nov 15 '17 at 00:20

2 Answers2

1

You should check NSDictionary one by one to prevent crash. Try my code below

NSDictionary *dictionary = json[@"data"];
NSString *output = @"";
if ([dictionary isKindOfClass:[NSDictionary class]]) {
  dictionary = dictionary[@"value"];
  if ([dictionary isKindOfClass:[NSDictionary class]]) {
    output = dictionary[@"url"];
  }
}
NSLog(@"%@", output);

You got crash because of calling valueForKey method on a NSString value. If someone says the reason for crash is call valueForKey when dictionary doesn't have this key, it's wrong. For more information Sending a message to nil in Objective-C

[dictionary isKindOfClass:[NSDictionary class]] always return NO if dictionary is nil so don't need to check dictionary in if statement. It's unnessary.

trungduc
  • 11,926
  • 4
  • 31
  • 55
-3

Your error means that json[@"data"]valueForKey:@"value"] doesn't NSDictionarry, so it have no @"url" key.

valueForKey it's KVC method, use objectForKey for dictionaries, and add more checks like:

        id url = nil;
        NSDictionary *data = [json objectForkey:@"data"];
        if ([data isKindOfClass:[NSDictionary class]]) {
            NSDictionary *value = [data objectForKey:@"value"];
            if ([value isKindOfClass:[NSDictionary class]]) {
                url = [value objectForKey:@"url"];
            }
        }
Roman Simenok
  • 530
  • 7
  • 22
  • @RomanSimenok you don't need to check `data` is `nil` because `[data isKindOfClass:[NSDictionary class]` will return `NO` if `data` is `nil` ;) – trungduc Nov 14 '17 at 16:39
  • @trungduc yes i know, but what faster check if it's nil or check if it's not kind of class?) – Roman Simenok Nov 14 '17 at 16:40
  • @RomanSimenok As i guess, check `nil` will be putted on the top of method `isKindOfClass`. So don't worry about which is faster. – trungduc Nov 14 '17 at 16:42
  • @RomanSimenok one more, both `valueForKey` and `objectForKey` return `nil` if if there is no object for key. His crash is because calling `valueForKey` on a `NSString`. Anw congratulation ;) – trungduc Nov 14 '17 at 17:00
  • as explained In this case the crash to call undefined method in a class....there is not much to add – kAiN Nov 14 '17 at 17:31
  • ad example... NSDictionary *dictionary = @{}; NSLog(@"%@", [dictionary valueForKey:@"213"]); it will log null and without crash. – kAiN Nov 14 '17 at 17:37