Paradigm Shift Design

ISHITOYA Kentaro's blog.

Twitter.frameworkでアップロードプログレスを取得する

前回と同じようなネタですが、
Twitter.frameworkを使ってイメージをポストする際に、公式のサンプルに従うとアップロードの進捗を知ることができないので、それをできるようにしようという話です。


公式のドキュメントには、

- (void)performTWRequestUpload
{
    NSURL *url = 
    [NSURL URLWithString:
            @"https://upload.twitter.com/1/statuses/update_with_media.json"];
    TWRequest *request = 
    	[[TWRequest alloc] initWithURL:url parameters:nil 
    		requestMethod:TWRequestMethodPOST];
    [request setAccount:[self.accounts objectAtIndex:0]];
    UIImage *image = [UIImage imageNamed:@"larry.png"];
    NSData *imageData = UIImagePNGRepresentation(image);
    [request addMultiPartData:imageData 
    		withName:@"media[]" type:@"multipart/form-data"];
    NSString *status = @"just setting up my twttr #iOS5";
    [request addMultiPartData:[status dataUsingEncoding:NSUTF8StringEncoding] 
    		withName:@"status" type:@"multipart/form-data"];
    [request performRequestWithHandler:
    ^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
    	NSDictionary *dict = 
    	(NSDictionary *)[NSJSONSerialization 
    		JSONObjectWithData:responseData options:0 error:nil];
    		dispatch_async(dispatch_get_main_queue(), ^{
    		});
    }];
}

という、短いコードが載っています(コメントは削除しました)がこれだとperformRequestWithHandlerがレスポンスを取得したときに、ブロックに入るので進捗が分かりません。


で、

には、

Alternatively, you can use the signedURLRequest method to create a request that you send using an NSURLConnection object.

と書いてあって、signedURLRequestをつかってリクエストするといいよとちょろっと書いてあります。このプロパティ、ググってみれば分かるのですが殆ど全く情報がありません。


performRequestWithHandlerの直前でsignedURLRequestの内容をみてもらえれば分かるのですが、TWRequestを使って作成された発行される前のNSURLRequestが入っています。なので、これを直接使えばNSURLConnectionをつかってリクエストできます。つまり、

NSURLConnection *connection = 
      [[NSURLConnection alloc] initWithRequest:request.signedURLRequest delegate:self];

とすれば、performRequestWithHandlerと同じ動作をします。公式サイトの例ならばイメージがポストされます。当然、NSURLConnectionDelegateやNSURLConnectionDataDelegateを実装して、その後の処理することができます。


ただし、不勉強なので原因は分かりませんが、ブロックの中で上記コードを動かすとDelegateメソッドが呼ばれません(多分スレッドが違うから)。なので、dispatch_asyncで呼び出すかperformSelectorOnMainThreadを使って呼び出す必要があります。私の実装では


Delegateを実装するクラスでNSURLDataDelegateを実装(delegateはimplementsなのか知らん?)

@interface TwitterPhotoSubmitter : 
PhotoSubmitter<PhotoSubmitterProtocol,
 NSURLConnectionDataDelegate,
 NSURLConnectionDelegate, 
 UIAlertViewDelegate>
@end

実装は次のように

- (void)startConnectionWithRequest:(NSURLRequest *)request{
    NSURLConnection *connection = 
      [[NSURLConnection alloc] initWithRequest:request delegate:self];
}

NSURLConnectionを生成するメソッドを用意し,

[self performSelectorOnMainThread:@selector(startConnectionWithRequest:) 
                       withObject: request.signedURLRequest 
                    waitUntilDone:NO];

として、ブロックの中でTWRequestを作り終わったあたりで呼び出します。


最後に、NSURLConnectionDataDelegateのメソッドを

- (void)connection:(NSURLConnection *)connection
   didSendBodyData:(NSInteger)bytesWritten 
 totalBytesWritten:(NSInteger)totalBytesWritten
totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite{
    NSLog(@"%f", (float)totalBytesWritten / (float)totalBytesExpectedToWrite);
}

とか実装すればいいです。


APIの仕様、それぞれに採用している方法が違って、複数のサービスを使うと思うとまじで面倒くさい...

せんでん

1タップで写真共有tottepost
カメラのついたiPad/iPhoneで撮った写真を、その場でFacebook/Mixi/DropboxなどのサービスにアップロードできるtottepostというiOSアプリを開発しています!詳しくは、iTunes App Storeをご覧ください。


ご購入はこちら!
1タップで写真共有 - tottepost - ISHITOYA Kentaro