もう5ヶ月も前になってしまいましたが、以前iPhoneアプリでTwitter連携するためのライブラリについて紹介しました。(OAuthでつぶやく)
https://github.com/jdg/oauthconsumer
こちらでダウンロードしたライブラリの取り込みかたについて記事にしたのですが、Twitterと連携できた喜びのあまり勢いだけで記事にしてしまった状態でした。
時間が空いてしまいましたが、フォロー記事を書いてみたいと思います。
結論からいいますと、前回記事に書いた内容の手順はいっさい行わなくても動作しました。
OAuth認証の段階として、oauth_verifierというユーザー情報のひとつを使用するのですが、前回の記事執筆時はまだライブラリに実装されていませんでした。でも最新のライブラリではちゃんと追加されています。ライブラリを持ってくれば、何も難しい手順なくTwitter連携できちゃいます。
これだけではなんなので、ちょっとしたサンプルアプリを作ってみたいと思います。
Twitterへの連携アプリの登録
ブラウザでTwitterを開き、設定>連携アプリ>開発者画面から、アプリ情報の登録を行います。
例では、Application TypeをBrowser、Access type をRead & Writeとします。
Application Typeは、iPhoneアプリなのでClientであるかに思えますが、Browserタイプが直接URLをコールバックしてくれるのに対して、Clientでは単にキーが表示され、ユーザーがそのキーを使って認証を行わないといけないという、あまり親切ではない設計になってしまうのす。
Callback URLはコーディングで使用します。OAuth認証は、最終的にこのURLを、対象ユーザーのアクセスキーをクエリとして返してくれます。URL事体はアプリで認識できればなんでもいいわけですが、やはり自分のアプリのサイトを指定したいものです。
また、URLを取得してからクエリを取れるわけなので、今回作ろうとしているサンプルでは、単純なUIWebViewを使用している以上、どうしても一瞬Callback URLの内容が表示されてしまいます。
プロジェクトの作成
Xcodeで新規にView-based Applicationを作成し、OauthconsumerTestという名前で保存しました。
OauthconsumerTestでは、ユーザーにブラウザを介してログイン認証してもらった後、ツイートするというシンプル機能を実装します。
「認証」ボタン、認証時に使用するwebビュー、ツイート内容を入力するテキストフィールド、それに「ツイート」ボタンの備わった画面を作成しておきます。
こんなかんじです。とにかく1画面で済ませるために、webビューオブジェクトはhiddenオブジェクトを使って必要なときだけ画面前面に出るようにしました。
ライブラリの取り込み
わたしの場合なのですが、今回のように階層分けされた複数のファイルを一度に取り込むときは、まずは直接プロジェクトフォルダにファイルそのものをコピーし、その後Xcodeから右クリック>追加>既存のファイル...とやったほうがやりやすいです。
実装
OAuthの認証は、以下の手順で行います。
1.Request token(D)
連携アプリ登録後に取得できるConsumer key(B)、Consumer secret(C)を使用してリクエスト。oauth_token、oauth_token_secretが帰ってきます。
2.Authorize(F)
取得したoauth_tokenをクエリとしてブラウザ表示。ユーザーにログインユーザーIDとパスワードを入力し、ログインしてもらう。正常にアプリが許可されると、コールバックURL(A)が表示されることになるので、アプリ側ではこれを取得する。
3.Access token(E)
コールバックURLに、oauth_token、oauth_verifierが付与されて帰ってくるので、これを使用してリクエスト。
このレスポンスとして、やっとoauth_token、oauth_token_secret、user_id、screen_nameが取得できます。ちなみに、コールバックURLにくっついてきたoauth_tokenと、Access tokenリクエストをして帰って来たoauth_tokenは値が違うので注意です。最終的に取得できたtokenとtoken_secretを使うことで、ツイートなどの各種操作ができます。
実装したコードは以下のとおりです。
OauthconsumerTestViewController.h
#import <UIKit/UIKit.h> //oaconsumer #import "OAConsumer.h" #import "OADataFetcher.h" #import "OAMutableURLRequest.h" @interface OauthconsumerTestViewController : UIViewController { IBOutlet UIButton* btnLogin; //ログインボタン IBOutlet UIWebView* oauthWebView; //認証用のブラウザ IBOutlet UILabel* lblUser; //USER表示 IBOutlet UILabel* lblAccessToken; //ACCESS_TOKEN表示 IBOutlet UILabel* lblAccessTokenSecret; //ACCESS_TOKEN_SECRET表示 IBOutlet UITextField* fieldTweetContent; //ツイート内容 IBOutlet UIButton* btnTweet; //ツイートボタン //ライブラリのオブジェクト OAConsumer *consumer; OAToken *accessToken; } @property (retain, nonatomic) UIButton* btnLogin; //ログインボタン @property (retain, nonatomic) UIWebView* oauthWebView; //認証用のブラウザ @property (retain, nonatomic) UILabel* lblUser; //USER表示 @property (retain, nonatomic) UILabel* lblAccessToken; //ACCESS_TOKEN表示 @property (retain, nonatomic) UILabel* lblAccessTokenSecret; //ACCESS_TOKEN_SECRET表示 @property (retain, nonatomic) UITextField* fieldTweetContent; //ツイート内容 @property (retain, nonatomic) UIButton* btnTweet; //ツイートボタン //ライブラリのオブジェクト @property (retain, nonatomic) OAConsumer *consumer; @property (retain, nonatomic) OAToken *accessToken; -(IBAction) btnLoginClicked:(id)sender;//ログインボタン押下 -(IBAction) btnTweetClicked:(id)sender;//ツイートボタン押下 @end
OauthconsumerTestViewController.m
#import "OauthconsumerTestViewController.h" @implementation OauthconsumerTestViewController @synthesize btnLogin, oauthWebView, lblUser, lblAccessToken, lblAccessTokenSecret, fieldTweetContent, btnTweet; @synthesize consumer,accessToken; #pragma mark - #pragma mark View lifecycle - (void)viewDidLoad { [super viewDidLoad]; //画面表示をクリア lblUser.text = @""; lblAccessToken.text = @""; lblAccessTokenSecret.text = @""; fieldTweetContent.text=@""; //最初は認証用のブラウザを表示しない oauthWebView.hidden = YES; } #pragma mark - #pragma mark button and screen click event //ログインボタン押下 -(IBAction) btnLoginClicked:(id)sender{ //承認開始 [self performSelector:@selector(startRequestToken)]; } //ツイートボタン押下 -(IBAction) btnTweetClicked:(id)sender{ //認証後に if ([@"" isEqualToString:lblAccessToken.text] == NO && [@"" isEqualToString:lblAccessTokenSecret.text] == NO) { //テキストフィールドの内容をツイート [self performSelector:@selector(tweet)]; } } #pragma mark - #pragma mark selectors for oauth requests //認証開始 1.Request token(D) -(void)startRequestToken { //CONSUMER_KEYとCONSUMER_SECRETの設定。Twitterのアプリケーション登録時に取得したものをセットしてください。 NSString *CONSUMER_KEY = [NSString stringWithString:@"51G2KX3PSGMpmiv2mAxQ"]; NSString *CONSUMER_SECRET = [NSString stringWithString:@"Mf8KMMtdGDB0NSBGEUeiKgSAn7YvpiHRUbMSW0yYrA"]; self.consumer = [[OAConsumer alloc] initWithKey:CONSUMER_KEY secret:CONSUMER_SECRET]; OADataFetcher *fetcher = [[[OADataFetcher alloc] init]autorelease]; NSURL *url = [NSURL URLWithString:@"https://api.twitter.com/oauth/request_token"]; OAMutableURLRequest *request = [[[OAMutableURLRequest alloc] initWithURL:url consumer:consumer token:nil realm:nil signatureProvider:nil]autorelease]; [request setHTTPMethod:@"POST"]; [fetcher fetchDataWithRequest:request delegate:self didFinishSelector:@selector(requestTokenTicket:didFinishGetRequestToken:) didFailSelector:@selector(requestTokenTicket:didFailTwitterSubmitterWithError:)]; } // 1.Request token(D) 正常時 2.Authorize(F) - (void) requestTokenTicket:(OAServiceTicket *)ticket didFinishGetRequestToken:(NSData *)data { if (ticket.didSucceed){ NSString *responseBody = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]autorelease]; self.accessToken = [[OAToken alloc] initWithHTTPResponseBody:responseBody]; NSString *address = [NSString stringWithFormat: @"https://api.twitter.com/oauth/authorize?oauth_token=%@&force_login=true", //@"https://api.twitter.com/oauth/authenticate accessToken.key]; NSURL *url = [NSURL URLWithString:address]; //ブラウザ表示 oauthWebView.hidden = NO; [oauthWebView loadRequest:[NSURLRequest requestWithURL:url]]; } else { //エラー処理 } } //エラー時 - (void) requestTokenTicket:(OAServiceTicket *)ticket didFailTwitterSubmitterWithError:(NSError *)error { //エラー処理 } // ブラウザから認証された場合 3.Access token(E) - (void) backFromBrowser:(NSURL *)responseURL { //クエリからaccess tokenを取得 self.accessToken = [self.accessToken initWithHTTPResponseBody:[responseURL query]]; OADataFetcher *fetcher = [[[OADataFetcher alloc] init]autorelease]; NSURL *url = [NSURL URLWithString:@"https://api.twitter.com/oauth/access_token"]; OAMutableURLRequest *request = [[[OAMutableURLRequest alloc] initWithURL:url consumer:consumer token:accessToken realm:nil signatureProvider:nil]autorelease]; [request setHTTPMethod:@"POST"]; [fetcher fetchDataWithRequest:request delegate:self didFinishSelector:@selector(requestTokenTicket:didFinishfetchAccessToken:) didFailSelector:@selector(requestTokenTicket:didFailTwitterSubmitterWithError:)]; } - (void) requestTokenTicket:(OAServiceTicket *)ticket didFinishfetchAccessToken:(NSData *)data { if (ticket.didSucceed){ NSString *responseBody = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]autorelease]; self.accessToken = [self.accessToken initWithHTTPResponseBody:responseBody]; //取得できた値を画面に表示する lblUser.text = self.accessToken.screen_name; lblAccessToken.text = self.accessToken.key; lblAccessTokenSecret.text = self.accessToken.secret; } else { //エラー処理 } } #pragma mark - #pragma mark UIWebView events // UIWebViewの内容がロード完了した時 - (void)webViewDidFinishLoad:(UIWebView *)webView { NSURL* url = [NSURL URLWithString:[webView stringByEvaluatingJavaScriptFromString:@"document.URL"]]; //認証了の場合にコールバックされるURL Twitterのアプリ登録時に設定したURLです。 NSURL *kanryoURL = [NSURL URLWithString:@"http://atsking.ats-japan.co.jp/~gau/index.html"]; //ロードされたURLと、コールバックされるURLのhostが同じなら、承認されたと見なす if ([[url host] isEqualToString:[kanryoURL host]]) { //次の処理へ [self performSelector:@selector(backFromBrowser:) withObject:url]; //webViewを隠す oauthWebView.hidden = YES; } } #pragma mark - #pragma mark tweet //ツイートする -(void)tweet { //ログイン認証された値を使用 self.accessToken = [[[OAToken alloc] initWithKey:lblAccessToken.text secret:lblAccessTokenSecret.text] autorelease]; NSURL *url = [NSURL URLWithString:@"http://api.twitter.com/1/statuses/update.json"]; OAMutableURLRequest* request = [[[OAMutableURLRequest alloc] initWithURL:url consumer:consumer token:accessToken realm:nil signatureProvider:nil] autorelease]; [request setHTTPMethod:@"POST"]; OARequestParameter *x1 = [[OARequestParameter alloc] initWithName:@"status" value:fieldTweetContent.text]; NSArray *params = [NSArray arrayWithObjects:x1, nil]; [request setParameters:params]; OADataFetcher *fetcher = [[[OADataFetcher alloc] init] autorelease]; [fetcher fetchDataWithRequest:request delegate:self didFinishSelector:@selector(ticket:didFinishWithTweet:) didFailSelector:@selector(ticket:didFailWithTweetError:)]; [x1 release]; } - (void)ticket:(OAServiceTicket *)ticket didFinishWithTweet:(NSData *)data { fieldTweetContent.text=@""; //ツイート完了を知らせる } - (void)ticket:(OAServiceTicket *)ticket didFailWithTweetError:(NSError *)error { //エラー処理 } // 以下省略 @end
oauthconsumerへのちょっとした追加
Access tokenのレスポンスには、ユーザー名(screen_id)も帰ってくるので、これを取得しておきたいです。
上記コード内の requestTokenTicket:ticket didFinishfetchAccessToken:dataメソッド内で、引数のdataを直接解析してもいいのですが、OATokenクラス内でデータの解析を行っているため、真似して追加してみます。
OAToken.h
クラスフィールドに NSString *screen_nameを追加します。
OAToken.m
- (id)initWithHTTPResponseBody:(const NSString *)body { //...中略 for (NSString *pair in pairs) { NSArray *elements = [pair componentsSeparatedByString:@"="]; if ([[elements objectAtIndex:0] isEqualToString:@"oauth_token"]) { aKey = [elements objectAtIndex:1]; //...中略 } else if ([[elements objectAtIndex:0] isEqualToString:@"oauth_token_renewable"]) { NSString *lowerCase = [[elements objectAtIndex:1] lowercaseString]; if ([lowerCase isEqualToString:@"true"] || [lowerCase isEqualToString:@"t"]) { renew = YES; } } //追加 else if ([[elements objectAtIndex:0] isEqualToString:@"screen_name"]) { self.screen_name = [elements objectAtIndex:1]; } } return [self initWithKey:aKey secret:aSecret session:aSession duration:aDuration attributes:attrs created:creationDate renewable:renew]; }
5が月前に取得したライブラリでは、oauth_verifierが取得できなかったため、同じようにして追加しました。今後twitter側で仕様が変更され、新たなキーを取得しないといけなくなったら、またこのクラスをいじればある程度対応できるのではないでしょうか。
実行
起動画面です。UIWebViewはhidden=YESによって表示されません。
「ログイン認証する」ボタンを押すことで、UIWebViewが表示されます。
ログインID、パスワードを入力し、「アプリを認証」ボタンを押します。
ログインできたので、Access トークンが取得できました。
さっそくつぶやいてみます。テキストフィールドに入力し、Tweetボタンを押下。
ツイートできたみたいです。
ごらんの通り。
プロジェクト一式
こちらからどうぞ。
OauthconsumerTest.zip