Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using objective-c blocks recursively with iOS Twitter API

So I'm trying to use the built in Twitter API in iOS 5 to retrieve a list of all the followers for a given user. In all the example documentation I can find, requests are made to the API passing inline blocks to be executed when the request returns, which is fine for most of the simpler stuff, BUT when I'm trying to get ~1000 followers, and the request is returning them paged in sizes ~100, I'm stuck on how to recursively call the request again using the 'next paging address' returned and processed inside the completion block. Here is the code:

- (void)getTwitterFollowers {
    //  First, we need to obtain the account instance for the user's Twitter account
    ACAccountStore *store = [[ACAccountStore alloc] init];
    ACAccountType *twitterAccountType = 
    [store accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];

    //  Request access from the user for access to his Twitter accounts
    [store requestAccessToAccountsWithType:twitterAccountType 
                     withCompletionHandler:^(BOOL granted, NSError *error) {
        if (!granted) {
            // The user rejected your request 
            NSLog(@"User rejected access to his account.");
        } 
        else {
            // Grab the available accounts
            NSArray *twitterAccounts = 
            [store accountsWithAccountType:twitterAccountType];

            if ([twitterAccounts count] > 0) {
                // Use the first account for simplicity 
                ACAccount *account = [twitterAccounts objectAtIndex:0];

                // Now make an authenticated request to our endpoint
                NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
                [params setObject:@"1" forKey:@"include_entities"];

                //  The endpoint that we wish to call
                NSURL *url = [NSURL URLWithString:@"http://api.twitter.com/1/followers.json"];

                //  Build the request with our parameter 
                request = [[TWRequest alloc] initWithURL:url 
                                           parameters:params 
                                        requestMethod:TWRequestMethodGET];

                [params release];

                // Attach the account object to this request
                [request setAccount:account];

                [request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
                    if (!responseData) {
                        // inspect the contents of error 
                        FullLog(@"%@", error);
                    } 
                    else {
                        NSError *jsonError;
                        followers = [NSJSONSerialization JSONObjectWithData:responseData 
                                                                  options:NSJSONReadingMutableLeaves 
                                                                    error:&jsonError];            
                        if (followers != nil) {                          
                            // THE DATA RETURNED HERE CONTAINS THE NEXT PAGE VALUE NEEDED TO REQUEST THE NEXT 100 FOLLOWERS, 
                            //WHAT IS THE BEST WAY TO USE THIS??
                            FullLog(@"%@", followers);
                        } 
                        else { 
                            // inspect the contents of jsonError
                            FullLog(@"%@", jsonError);
                        }
                    }
                }];         
            } // if ([twitterAccounts count] > 0)
        } // if (granted) 
    }];
    [store release];
}

Ideally I'd like some way to listen for this data being returned, check for a next page value and if it exists, reuse the code block and append the data returned. I', sure there must be a 'best-practice' way to achieve this, any help would be much appreciated!

like image 282
Sam Clewlow Avatar asked Jun 06 '12 10:06

Sam Clewlow


2 Answers

To use any block recursively you have to declare it first and define it later. Try this:

__block void (^requestPageBlock)(NSInteger pageNumber) = NULL;

requestPageBlock =  [^(NSInteger pageNumber) {
    // do request with some calculations 
    if (nextPageExists) {
        requestPageBlock(pageNumber + 1);
    }
} copy];

// now call the block for the first page
requestPageBlock(0);
like image 90
Eimantas Avatar answered Oct 21 '22 03:10

Eimantas


To expand on @Eimantas' answer, your request handler is expecting a specific block signature, so you need a different way to handle the page number.

-(void)getTwitterFollowers {
    // set up request...
    __block int page = 0;
    __block void (^requestHandler)(NSData*, NSHTTPURLResponse*, NSError*) = null;
    __block TWRequest* request = [[TWRequest alloc] initWithURL:url 
                                                     parameters:params 
                                                  requestMethod:TWRequestMethodGET];
    requestHandler = [^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
        followers = [NSJSONSerialization JSONObjectWithData:responseData 
                                          options:NSJSONReadingMutableLeaves 
                                            error:&jsonError];            
        if (followers != nil) {                          
            // process followers
            page++;
            NSMutableDictionary *params = [NSMutableDictionary dictionaryWithDictionary:request.parameters];   
            // update params with page number
            request = [[TWRequest alloc] initWithURL:url 
                                          parameters:params 
                                       requestMethod:TWRequestMethodGET];
            [request performRequestWithHandler:requestHandler];
        } 
    } copy];

    // now call the block for the first page
    [request performRequestWithHandler:requestHandler];
}
like image 42
Christopher Pickslay Avatar answered Oct 21 '22 03:10

Christopher Pickslay