0

I am making a call to twitters API to load some tweets for a specific section of my app.

A small chunk of users are reporting a crash when loading the tweets view, while the rest have no problem at all.

I have submitted the code to Apple Tech Support and they responded letting me know that NSJSONSerialization can sometimes return a NSArray or NSDictionary.

Obviously it will throw an error is objectAtIndex: is called on an NSDictionary object, which I believe is the culprit for all of my users.

The partial solution is to detect if it is an Array or NSDictionary.

Here is where I am at now:

id feedData = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&jsonError];

if ([feedData isKindOfClass:[NSArray class]]) {
     //Is array


} else if ([feedData isKindOfClass:[NSDictionary class]]) {
     //is dictionary


} 

I basically need an NSArray every single time. So in the is array block, I basically just use the feedData, but in NSDictionary, how can I convert it to an NSArray that will match the structure I need.

Honestly the biggest issue is that I cannot see what the NSDictionary structure looks like because none of my testing devices or simulator return the NSDictionary data, they all return an NSArray.

Here is what the entire getUserFeed method that sends the request to twitter looks like:

// Get the twitter feed
    NSURL *requestURL = [NSURL URLWithString:TW_API_TIMELINE];

    // Set up proper parameters
    NSMutableDictionary *timelineParameters = [[NSMutableDictionary alloc] init];
    [timelineParameters setObject:kNumTweets forKey:@"count"];
    [timelineParameters setObject:@"1" forKey:@"include_entities"];

    // Create the Social Request
    SLRequest *postRequest = [SLRequest requestForServiceType:SLServiceTypeTwitter requestMethod:SLRequestMethodGET URL:requestURL parameters:timelineParameters];
    postRequest.account = self.delegate.userAccount;

    // Perform the request
    [postRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
        dispatch_async(dispatch_get_main_queue(), ^{
            // Check if we reached the reate limit
            if ([urlResponse statusCode] == 429) {

                // Rate limit reached
                // Display an alert letting the user know we have hit the rate limit

                UIAlertView *twitterAlert = [[UIAlertView alloc] initWithTitle:kRateLimitTitle
                                                                       message:kRateLimitMessage
                                                                      delegate:nil
                                                             cancelButtonTitle:@"Ok"
                                                             otherButtonTitles:nil];
                [twitterAlert show];


                // Stop animating the pull to refresh if it is animating
                [self.feedTableView.pullToRefreshView stopAnimating];

                return;

            }
            // Check if there was an error
            if (error) {

                NSLog(@"Error: %@", error.localizedDescription);

                // Stop animating the pull to refresh if it is animating
                [self.feedTableView.pullToRefreshView stopAnimating];

                return;

            }
            // Check if there is some response data
            if (responseData) {
                NSError *jsonError = nil;

                id feedData = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableContainers error:&jsonError];

                if ([feedData isKindOfClass:[NSArray class]]) {
                    //Is array
                    NSLog(@"It's an Array");

                } else if ([feedData isKindOfClass:[NSDictionary class]]) {
                    //Is dictionary
                    NSLog(@"It's a Dictionary");




                } else {
                    //is something else


                }

                if (!jsonError) {

                    [self gatherTweetsFromArray:feedData];

                } else {

                    // Stop animating the pull to refresh if it is animating
                    [self.feedTableView.pullToRefreshView stopAnimating];


                    // Alert the user with the error
                    UIAlertView *twitterAlert = [[UIAlertView alloc] initWithTitle:kErrorTitle
                                                                           message:kErrorMessage
                                                                          delegate:nil
                                                                 cancelButtonTitle:@"Ok"
                                                                 otherButtonTitles:nil];
                    [twitterAlert show];


                }
            } else {

                // Stop animating the pull to refresh if it is animating
                [self.feedTableView.pullToRefreshView stopAnimating];

                // Alert the user with the error
                UIAlertView *twitterAlert = [[UIAlertView alloc] initWithTitle:kErrorTitle
                                                                       message:kErrorMessage
                                                                      delegate:nil
                                                             cancelButtonTitle:@"Ok"
                                                             otherButtonTitles:nil];
                [twitterAlert show];


            }


        });
    }];

This is a MAJOR bug and I need to squash it, so any ideas or information will be greatly appreciated! Thank you!

Kyle Begeman
  • 7,169
  • 9
  • 40
  • 58
  • with no idea of the structure of the dictionary there's is no way for a perfect solution. I assume - since only some users report the crash - that the dictionary structure is returned in exceptional cases, e.g. when the twitter server has an internal error. I think you're fine when you show an "unknown error alert" whenever anything else than an array is returned. Additionally you could do some crash reporting, either with a third party chrash reporter (Crashlytics recommended) or with an self made solution, ... – Kai Huppmann Dec 11 '13 at 22:50
  • ... e.g. asking the user to send you an email to which the debug description of the dictionary is automatically attached. – Kai Huppmann Dec 11 '13 at 22:50
  • I posted some code displaying the actual call block to the Twitter API. If it was an internal error from Twitter, should it only affect certain users every single time? It is a small number, but it is consistent and never actually works. Thanks for the info and Crashlytics link, I will be looking into that for sure! – Kyle Begeman Dec 11 '13 at 23:36
  • I didn't get that few users have the problem consistently. Do you know anymore of these users? if so, just look for things they've in common (iOS-Version, Firewall, ...?) in order to be able to reproduce the error. – Kai Huppmann Dec 11 '13 at 23:44
  • Are you familiar with how I can compile the feedData into a string that can be emailed and readable? This seems like the most logical way to at least read the error the user is receiving. – Kyle Begeman Dec 12 '13 at 00:52
  • just use the stringWithFormat and the %@ to have a not-so-beautiful-but-working description of either NSArray or NSDictionary. Maybe here's some hints for beautification: http://stackoverflow.com/questions/7121120/beautify-nslog-of-nsarray-nsdictionary – Kai Huppmann Dec 12 '13 at 10:02
  • The ugly description worked just fine. It looks like it is an error being passed by twitter which returns as an NSDictionary. Everyone so far who has had crashing has had the same error. The error is error code 220, Your credentials are not allowed access to this request, which is a very odd error for an iOS API call to get. But thats a whole other issue I am not dealing with. Thanks for all your help! – Kyle Begeman Dec 12 '13 at 12:37
  • Your welcome. Maybe you should post all you found out as an answer to your own question – Kai Huppmann Dec 12 '13 at 13:32

0 Answers0