Supertecnoboff
Supertecnoboff

Reputation: 6606

Check if JSON value exists - iOS

I have an iOS application which downloads and parses a Twitter JSON feed and then presents that feed in a UITableView. This all works fine but I have one question:

When the user taps a UITableView cell, the app will look into the array "tweets_links" and see if that particular tweet has an attached URL, if it does then the web view will appear.

Because not all tweets have website URLs, I have added a simple try catch statement (like in C++) which can tell me if there is an exception when trying to access that part of the array.

My question is: is this is good or bad approach to doing this??

Here is my code:

int storyIndex = indexPath.row;
int url_test = 1;
NSString *url;

@try {
    url = [[tweets_links[storyIndex] valueForKey:@"url"] objectAtIndex:0];
}

@catch (NSException *problem) {
    // There is NO URL to access for this Tweet. Therefore we get the out of bounds error.
    // We will NOT take the user to the web browser page.
    // Uncomment the line below if you wish to see the out of bounds exception.
    // NSLog(@"%@", problem);
    url_test = 0;
}

if (url_test == 1) {
    WebBrowser *screen = [[WebBrowser alloc] initWithNibName:nil bundle:nil];
    self.seconddata = screen;
    seconddata.web_url = url;
    screen.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
    [self presentViewController:screen animated:YES completion:nil];
}

else if (url_test == 0) {
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Info" message:@"There is no URL attatched to this Tweet." delegate:self cancelButtonTitle:@"Dismiss" otherButtonTitles:nil];
    [alertView show];

    [tweetTableView deselectRowAtIndexPath:indexPath animated:YES];
}

Is there a much better way of trying to achieve what I am doing???

Thanks, Dan.

Upvotes: 0

Views: 1615

Answers (5)

Javier Cancio
Javier Cancio

Reputation: 1438

Since url value is a NSString value, you could use length to check both if it's nil and if not, if it has any value (not empty string). You can check then if this NSString is a valid url.

  - (BOOL) validateUrl: (NSString *) candidate {
        NSString *urlRegEx = @"(http|https)://((\\w)*|([0-9]*)|([-|_])*)+([\\.|/]((\\w)*|([0-9]*)|([-|_])*))+";
        NSPredicate *urlTest = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", urlRegEx]; 
        return [urlTest evaluateWithObject:candidate];
    }

....

NSString *url = [[tweets_links[storyIndex][@"url"]] firstObject];

if ([url length] && [self validateUrl: url]) {
 // Has a valid URL
}

Upvotes: 0

Harsh
Harsh

Reputation: 2908

Using either if the below approaches will be fine because TRY CATCH is used to catch programming errors and use objectForKey: instead of valueForKey:

if ([tweets_links[storyIndex] objectForKey:@"url"] != nil)

OR

if ([url isKindOfClass:[NSString class]])
{
// Code handling the URL
}
else
{
// Code handling there is no URL
}

Upvotes: 1

gnasher729
gnasher729

Reputation: 52538

Using exceptions in Objective-C is throwned upon. Exceptions are reserved for programming errors. You don't catch them, you fix the code. With a JSON document, you never have any guarantees what you received, so just be careful.

NSString* url = nil;
NSArray* linksArray = nil;
NSDictionary* linkDict = nil;
NSArray* urlArray = nil;

if ([tweet_links isKindOfClass:[NSArray class]])
    linksArray = tweet_links;

if (storyIndex >= 0 && storyIndex < linksArray.count)
    linkDict = linksArray [storyIndex];

urlArray = linkDict [@"url"];
if ([urlArray isKindOfClass:[NSArray class]] && urlArray.count > 0)
    url = urlArray [0];

if ([url isKindOfClass:[NSString class]])
{
    // Code handling the URL
}
else
{
    // Code handling there is no URL
}

Note that sending messages to a nil object always returns 0 / NO / nil as appropriate.

And please get into the habit of naming variables properly. You wrote "int url_test = 1;". What does url_test mean? I read the variable name, I have no idea what it means. I need to understand all the code. Making it "int" means it could be 0, 1, 2, 20000 or whatever. If you write instead "BOOL urlValid = YES;" that is clear: It means that you have a valid URL.

Upvotes: 0

Abizern
Abizern

Reputation: 150615

Using try and catch is Objective-C isn't encouraged there are other ways checking and handling errors

// firstObject will return the first object in the array or nil if the array is empty.
url = [[tweets_links[storyIndex][@"url"]] firstObject];

if (!url) {
    // handle the case for no url
} else {
    // do something with url
}

Since sending a message to nil is safe in Objective-C and returns nil it's safe to chain calls. e.g. If the dictionary didn't have an object for that key, then it would return nil and sending firstObject to nil returns nil.

Upvotes: 5

Aaron
Aaron

Reputation: 7145

I don't know a ton about the Twitter feed, but you can probably check for a nil value returned from objectForKey: like so

if ([tweets_links[storyIndex] objectForKey:@"url"] != nil) { /* process the URL */ }

Your code assumes that the value is always an array of at least size = 1, it would be safer to inspect the @"url" key's value before assuming it's an array.

Upvotes: 0

Related Questions