2
votes

I need to update my app with Twitter API 1.1. Previously I retrieved tweets using following URL:

http://search.twitter.com/search.json?tag=ios&rpp=25

But now it does not work. I tried to use following URL from Twitter API documentation:

https://api.twitter.com/1.1/search/tweets.json?q=%23freebandnames&since_id=24012619984051000&max_id=250126199840518145&result_type=mixed&count=4.

But it returns the following error:

{"errors":[{"message":"Bad Authentication data","code":215}]}

How can I retrieve these tweets?

I would like to use Twitter.framework, but any suggestions will be appreciated.

2
This is not exactly what I want. But it's a good start point. Thanks a lot. - Sviatoslav Yakymiv
I know... :)... Good luck! - TonyMkenu

2 Answers

6
votes

I've found a solution. Twitter accepts two types of authentication for tweest retrieving:

  1. Application-user authentication
  2. Application-only authentication The first type requires login and pass and the second does not require, but it requires bearer token.

At first my request looked in following way:

NSURL *URL = [NSURL URLWithString:@"http://search.twitter.com/search.json?tag=ios&rpp=25"];
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:URL cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:30.0];   
NSData *data = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:nil error:nil];

Now it looks as follows:

if(self.bearerToken == nil) return;
NSURL *URL = [NSURL URLWithString:@"https://api.twitter.com/1.1/search/tweets.json?q=%%23ios&count=25"];
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:URL cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:30.0];
[urlRequest setValue:[NSString stringWithFormat:@"Bearer %@", self.bearerToken] forHTTPHeaderField:@"Authorization"];
NSData *data = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:nil error:nil];

Also this method requires to add two methods bearerToken and _base64Encode:

- (NSString *)bearerToken
{
    if(_bearerToken == nil)
    {
        NSString * consumerKey = [config.consumerKey stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
        NSString * consumerSecret = [config.consumerSecret stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

        //the combined authentication key is "CONSUMER_KEY:CONSUMER_SECRET" run through base64 encoding.
        //we'll use NSData instead of NSString here so that we can feed it directly to the HTTPRequest later.
        NSString * combinedKey = [[self class] _base64Encode:[[NSString stringWithFormat:@"%@:%@", consumerKey, consumerSecret] dataUsingEncoding:NSUTF8StringEncoding]];

        NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://api.twitter.com/oauth2/token"]];
        [urlRequest setHTTPMethod:@"POST"];
        [urlRequest setValue:[NSString stringWithFormat:@"Basic %@", combinedKey] forHTTPHeaderField:@"Authorization"];
        [urlRequest setValue:[NSString stringWithFormat:@"application/x-www-form-urlencoded;charset=UTF-8"] forHTTPHeaderField:@"Content-Type"];
        [urlRequest setHTTPBody:[@"grant_type=client_credentials" dataUsingEncoding:NSUTF8StringEncoding]];
        NSData *data = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:nil error:nil];
        NSDictionary *responseJSON = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
        _bearerToken = [responseJSON valueForKey:@"access_token"];
    }
    return _bearerToken;
}
+(NSString *)_base64Encode:(NSData *)data{
    //Point to start of the data and set buffer sizes
    int inLength = [data length];
    int outLength = ((((inLength * 4)/3)/4)*4) + (((inLength * 4)/3)%4 ? 4 : 0);
    const char *inputBuffer = [data bytes];
    char *outputBuffer = malloc(outLength);
    outputBuffer[outLength] = 0;

    //64 digit code
    static char Encode[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

    //start the count
    int cycle = 0;
    int inpos = 0;
    int outpos = 0;
    char temp;

    //Pad the last to bytes, the outbuffer must always be a multiple of 4
    outputBuffer[outLength-1] = '=';
    outputBuffer[outLength-2] = '=';

    /* http://en.wikipedia.org/wiki/Base64
     Text content   M           a           n
     ASCII          77          97          110
     8 Bit pattern  01001101    01100001    01101110

     6 Bit pattern  010011  010110  000101  101110
     Index          19      22      5       46
     Base64-encoded T       W       F       u
     */


    while (inpos < inLength){
        switch (cycle) {
            case 0:
                outputBuffer[outpos++] = Encode[(inputBuffer[inpos]&0xFC)>>2];
                cycle = 1;
                break;
            case 1:
                temp = (inputBuffer[inpos++]&0x03)<<4;
                outputBuffer[outpos] = Encode[temp];
                cycle = 2;
                break;
            case 2:
                outputBuffer[outpos++] = Encode[temp|(inputBuffer[inpos]&0xF0)>> 4];
                temp = (inputBuffer[inpos++]&0x0F)<<2;
                outputBuffer[outpos] = Encode[temp];
                cycle = 3;
                break;
            case 3:
                outputBuffer[outpos++] = Encode[temp|(inputBuffer[inpos]&0xC0)>>6];
                cycle = 4;
                break;
            case 4:
                outputBuffer[outpos++] = Encode[inputBuffer[inpos++]&0x3f];
                cycle = 0;
                break;
            default:
                cycle = 0;
                break;
        }
    }
    NSString *pictemp = [NSString stringWithUTF8String:outputBuffer];
    free(outputBuffer);
    return pictemp;
}

Response has been changed too, so I had to change parser too. Previous version:

NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithString:jsonStr];
if (responseDictionary)
{
    id data = responseDictionary[@"results"];
    if ([data isKindOfClass:NSArray.class])
    {
        NSArray *dataArray = (NSArray*)data;
        for (NSDictionary *post in dataArray)
        {
                avatarUrl = post[@"profile_image_url"];
            author = post[@"from_user"];
            message = post[@"text"];

                NSDateFormatter *inputFormatter = [[NSDateFormatter alloc] init];
                [inputFormatter setDateFormat:@"EEE',' dd MMM yyyy HH:mm:ss ZZZZ"];
            date = [inputFormatter dateFromString: post[@"created_at"]];
        }
    }
}

Current version:

NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithString:jsonStr];
if (responseDictionary)
{
    id data = [responseDictionary valueForKey:@"statuses"];
    if ([data isKindOfClass:NSArray.class])
    {
        NSArray *dataArray = (NSArray*)data;
        for (NSDictionary *post in dataArray)
        {               
            avatarUrl = post[@"user"][@"profile_image_url"];
            author = post[@"user" ][@"name"];;
            message = post[@"text"];         
                    NSDateFormatter *inputFormatter = [[NSDateFormatter alloc] init];
                    [inputFormatter setDateFormat:@"EEE MMM dd HH:mm:ss ZZZZ yyyy"];
                date = [inputFormatter dateFromString: post[@"created_at"]];
        }
    }
}
1
votes

As per this doc it is not possible to fetch Hashtag feed without Authentication.

But you can manage it at server side. At server side you can Authenticate on Twitter and get the Hash tag feed from Twitter (Authenticate is also required here to get feed) . So Whenever user not Authenticate on iPhone that time you have to call Web Service and web Service will return the HashTag feed which you have manage at your server side code.

And If use already Authenticate on iOS Device (iPhone) than you can get the feed using iOS.