2

I want to make an app that uses on the Google Reader API. But I'm finding out that there isn't an offical API for it - is there a problem using the unofficial API, in terms of App Store guidelines/approval? Would other apps (Reeder, etc) use this?

Also what is the best method for logging in? Is OAuth the preffered method? Is using Janrain a good idea?

daihovey
  • 3,485
  • 13
  • 66
  • 110

2 Answers2

7

Frankly Apple doesn't care if you use Google's unofficial API.

I worked for a customer on a RSS reader app that used Google Reader for syncing. We didn't use OAuth but the standard HTTP login which returns you a cookie where you'll have to extract a token from to use in consecutive calls to the various reader URLs.

I can post you the login code from my (old) proof of concept app. It uses ASIHTTP and some custom string categories. The idea is to send a login request, get the response and extract the session ID/auth code from the response's cookie header. Then you can use that session ID/auth code for consecutive calls.

#pragma mark -
#pragma mark login

//this is your sessionID token you get from the login
//use this in consecutive calls to google reader
//this method returns you the header string you have to add to your request
//[request addRequestHeader: @"Cookie" value: [self sidHeader]];
- (NSString *) sidHeader
{
    return [NSString stringWithFormat: @"SID=%@", [self sid]];
}

- (NSString *) authHeader
{
    return [NSString stringWithFormat: @"GoogleLogin auth=%@",[self auth]];
}


//login to your google account and get the session ID
- (void) login
{
    NSString *username = @"my.googlelogin@gmail.com";
    NSString *password = @"mypassword123";
    NSString *loginUrl = @"https://www.google.com/accounts/ClientLogin?client=NNW-Mac";
    NSString *source = @"NNW-Mac"; //let's fake NetNewsWire
    NSString *continueUrl = @"http://www.google.com";

    ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString: loginUrl]]; // log in & get cookies
    [request addRequestHeader: @"User-Agent" value: @"NetNewsWire/3.2b25 (Mac OS X; http://www.newsgator.com/Individuals/NetNewsWire/)"];

    [request setPostValue: username forKey: @"Email"];
    [request setPostValue: password forKey: @"Passwd"];
    [request setPostValue: @"reader" forKey: @"service"];
    [request setPostValue: source forKey: @"source"];
    [request setPostValue: continueUrl forKey: @"continue"];

    [request setDelegate: self];
    [request setDidFailSelector: @selector(loginRequestFailed:)];
    [request setDidFinishSelector: @selector(loginRequestFinished:)];

    [request start];
}   

-(void)loginRequestFinished:(ASIHTTPRequest *)request
{
    NSString *responseString = [request responseString];

    //login failed
    if ([responseString containsString: @"Error=BadAuthentication" ignoringCase: YES])
    {
        [self setLastError: [self errorWithDescription: @"Bad Username/Passsword" code: 0x001 andErrorLevel: 0x00]];

        if ([delegate respondsToSelector: @selector(gReaderLoginDidFail:)])
        {
            [delegate gReaderLoginDidFail: self];
        }

        return NO;
    }

    //captcha required
    if ([responseString containsString: @"CaptchaRequired" ignoringCase: YES])
    {
        [self setLastError: [self errorWithDescription: @"Captcha Required" code: 0x001 andErrorLevel: 0x00]];

        if ([delegate respondsToSelector: @selector(gReaderLoginDidFail:)])
        {
            [delegate gReaderLoginDidFail: self];
        }

        return NO;
    }

    //extract SID + auth
    NSArray *respArray = [responseString componentsSeparatedByCharactersInSet: [NSCharacterSet newlineCharacterSet]];

    NSString *sidString = [respArray objectAtIndex: 0];
    sidString = [sidString stringByReplacingOccurrencesOfString: @"SID=" withString: @""];
    [self setSid: sidString];

NSString *authString = [respArray objectAtIndex: 2];
authString = [authString stringByReplacingOccurrencesOfString: @"Auth=" withString: @""];
[self setAuth: authString];
    //mesage delegate of success
    if ([delegate respondsToSelector: @selector(gReaderLoginDidSucceed:)])
    {
        [delegate gReaderLoginDidSucceed: self];
    }

    return YES;
}

- (void)loginRequestFailed:(ASIHTTPRequest *)request
{
    NSError *error = [request error];

    //NSLog(@"login request failed with error: %@", [error localizedDescription]);
    [self setLastError: error];

    if ([delegate respondsToSelector: @selector(gReaderLoginDidFail:)])
    {
        [delegate gReaderLoginDidFail: self];
    }

}

After login you can use sid and auth to forge requests to the Reader's API endpoints.

Example:

- (ASIHTTPRequest *) requestForAPIEndpoint: (NSString *) apiEndpoint
{
    ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString: apiEndpoint]];
    [request addRequestHeader: @"User-Agent" value: @"NetNewsWire/3.2b25 (Mac OS X; http://www.newsgator.com/Individuals/NetNewsWire/)"];
    [request addRequestHeader: @"Cookie" value: [self sidHeader]];
    [request addRequestHeader: @"Authorization" value: [self authHeader]];

    return request;
}

An interesting read about Google Reader and its private API is http://timbroder.com/2007/08/google-reader-api-functions.html

Please make sure to read the latest comments :)

/edit: I updated the code to use the auth header (which google introduced in june this year). I guess this would be the place to put your OAuth token in if you would use OAuth. guess

  • Thanks for that Jaroslaw! I'm worried about using an unofficial API for an app. Should I be? – daihovey Sep 23 '10 at 00:05
  • Yes and No :) No because Apple does not care if you use someone else's unofficial APIs (they care only for their own APIs). So you won't get any trouble from Apple if you try to put a Google Reader app into the store.\n\n But there's the problem with unofficial APIs in general as they can change any moment. For example: My example code I posted here first was broken and I had to fix it because Google decided to use the new "Auth" parameter for auth and ignore the SID. Supporting an unofficial 3rd party API is always work and you should be aware of that. –  Sep 23 '10 at 10:40
  • Hmm yeh what a pain. But you think apps like Reedera and all have the same problem? I wont mind if all other Reader apps fail at the same time :) – daihovey Sep 23 '10 at 23:18
  • hey is ASIFormDataRequest and the other library files acceptable by apple? Im confused by their policy. Ive heard they decline apps for using external libraries. Isnt this one of them? – daihovey Oct 25 '10 at 01:26
  • Where can I get "source". DO i need to register my app somewhere on google's site? – Shri Nov 19 '11 at 10:14
  • I am getting sid and auth correctly. But when i try to access reader's endPoints, i always get requestFailed. Here's the code... ASIHTTPRequest *request = [self requestForAPIEndpoint:@"https://www.google.com/reader/api/0/subscription/list?output=json"]; [request setDelegate:self]; [request startAsynchronous]; Everytime I get callback in delegate method - (void)requestFailed:(ASIHTTPRequest *)request. Why is this? sid and auth are properly assigned with the header. – Shri Nov 19 '11 at 10:24
6

Ive since found this: "The Google Data APIs Objective-C Client Library provides an iPhone static library, a Mac OS X framework, and source code that make it easy to access data through Google Data APIs. " code.google.com/p/gdata-objectivec-client - which is great! It doesn't include the Reader API however (because it's not been released).

I have been able to access the API by changing (in the OAuthSampleTouch example)

NSString *scope = @"http://www.google.com/m8/feeds/";

in OAuthSampleRootViewControllerTouch.m to

NSString *scope = @"http://www.google.com/reader/api/*";

and

urlStr = @"http://www.google.com/m8/feeds/contacts/default/thin";

to

urlStr = @"http://www.google.com/reader/atom/user/-/label/Design";

where Design is a folder name - check this http://code.google.com/p/pyrfeed/wiki/GoogleReaderAPI its a great help.


Update

I have since found that this technique to be the best / lightest / less-complicated : Native Google Reader iPhone Application

Community
  • 1
  • 1
daihovey
  • 3,485
  • 13
  • 66
  • 110