UrlConnection を使ってみた
bonjoure とか、BSD ソケットだとか、通信の方法は色々とあるようですが、さっくり使う分にはNSUrlConnection がお手軽でよいと思いました.
テスト用にアプリケーションで利用するリソースを入れ替えるぐらいだったら、NSUrlConnection でも十分実用になるもんです.
そんなわけで、足跡をぺたぺたと..
非同期通信
接続する url を、NSURLRequest で定義して、NSURLResponse と NSError と一緒に、NSURLConnection に渡すだけで、通信が行える.
非同期通信(通信が終了するまで、処理が進まない)場合には、以下のコードだけでよい.
NSString* a_url = @"http://hogehoge/hoge.jpg"; NSURLRequest* a_request = [NSURLRequest requestWithURL:[NSURL URLWithString:a_url]]; NSURLResponse* a_response; NSError* a_error; NSData* a_data = [NSURLConnection sendSynchronousRequest:a_request returningResponse:&a_response error:&a_error];
NSURLRequest には、直接 url を記述した文字列を渡すのではなく、NSURL として渡す.
NSURLConnection では、http://〜 というurlの形式だけではなく、ローカルファイルを示す、file://〜 という形でのアクセスも可能.
恐らく、ftp://〜 とか smb://〜 とかいう形式でも接続できると思うけど、当面利用しなさそうなので未検証.
NSURLResponse を利用して、サーバから返された情報にアクセスできる.
NSLog( @"size = %d", [a_response expectedContentLength] ); // サイズの取得 NSLog( [a_response MIMEType] ); // mime-Type NSLog( [a_response textEncodingName] ); // テキストエンコーディング
今回は特に利用しなかったので、取得したデータを NSLog で表示しただけ...
非同期通信
非同期通信の場合には、 delegate を用意する必要がある.
ヘルプを眺めたとき、 「delegate を指定する必要があるのは分かったんだけれども、プロトコルはどこに定義してあんだよっ」なんてことで悩んでいたんだけれども、プロトコルは Objective-C2.0 で追加された仕様なのかそんなもんは必要なかった.
単純に、デリゲートとしたオブジェクトに、必要な delegate の関数をぺたぺた書いていけばよい.
私は、適当に View に定義しておいた.
必要ありそうな delegate 関数(言い方あってるのか?)は、以下になる
/// サーバからレスポンスが送られてきたときのデリゲート - (void)connection:(NSURLConnection *)i_connection didReceiveResponse:(NSURLResponse *)i_response; /// サーバからデータが送られてきたときのデリゲート - (void)connection:(NSURLConnection *)i_connection didReceiveData:(NSData *)i_data; /// データのロードか完了した時のデリゲート - (void)connectionDidFinishLoading:(NSURLConnection *)i_connection; /// サーバからエラーが返されたときのデリゲート - (void)connection:(NSURLConnection *)i_connection didFailWithError:(NSError *)i_error;
プロトコル部分で悩んだので、念のため View のヘッダーも貼っておくことにする.
@interface MainView : UIView { UIImage* m_image; NSMutableData* m_data; } - (IBAction) getImage:(id)i_sender; /// サーバからレスポンスが送られてきたときのデリゲート - (void)connection:(NSURLConnection *)i_connection didReceiveResponse:(NSURLResponse *)i_response; /// サーバからデータが送られてきたときのデリゲート - (void)connection:(NSURLConnection *)i_connection didReceiveData:(NSData *)i_data; /// データのロードか完了した時のデリゲート - (void)connectionDidFinishLoading:(NSURLConnection *)i_connection; /// サーバからエラーが返されたときのデリゲート - (void)connection:(NSURLConnection *)i_connection didFailWithError:(NSError *)i_error; @end
ほら、プロトコルなんて出てこない.
接続されると、適当なタイミングで connection:didReceiveData に受信した分だけのデータが渡されるので(つまり、ちぎれたデータが飛んでくる) NSMutableData なんかを利用して、データを保持しておけばよい.
実際の接続の方法は、次の通り
- (IBAction) getImage:(id)i_sender { NSString* a_url = @"http://hogehoge/hoge.jpg"; NSURLRequest* a_request = [NSURLRequest requestWithURL:[NSURL URLWithString:a_url]]; [NSURLConnection connectionWithRequest:a_request delegate:self]; }
あとは、デリゲートが呼び出されるタイミングで、適切な処理をすればよい.
/// サーバからレスポンスが送られてきたときのデリゲート - (void)connection:(NSURLConnection *)i_connection didReceiveResponse:(NSURLResponse *)i_response { NSLog( @"size = %d", [i_response expectedContentLength] ); // サイズの取得 NSLog( [i_response MIMEType] ); // mime-Type NSLog( [i_response textEncodingName] ); // テキストエンコーディング // データ蓄積用に NSMutableData を初期化 m_data = [[NSMutableData alloc] init]; }
最初に呼び出されるのが、 connection:didReceiveResponse となる.
サーバ側がサボらずに、ここで適切なサイズを返してくれているんだったら、NSMutableData なんか使う必要は無い.
/// サーバからデータが送られてきたときのデリゲート - (void)connection:(NSURLConnection *)i_connection didReceiveData:(NSData *)i_data { [m_data appendData:i_data]; }
次に来るのは、 connection:didReceiveData が順当.
こいつは、適当な頻度で必要な回数呼ばれる点に注意する必要がある.
(繰り返しになるけれども、データは一度に来ない)
/// データのロードか完了した時のデリゲート - (void)connectionDidFinishLoading:(NSURLConnection *)i_connection { // 取得したデータを保存する NSString* a_home_dir = NSHomeDirectory(); NSString* a_doc_dir = [a_home_dir stringByAppendingPathComponent:@"Documents"]; NSFileManager* a_file_mgr = [NSFileManager defaultManager]; [a_file_mgr changeCurrentDirectoryPath:a_doc_dir]; [a_file_mgr createFileAtPath:@"unsync-image.jpg" contents:m_data attributes:nil]; [m_data release]; m_data = nil; }
そして、取得の終了.
ここではファイルに書き込んでいるけれども、あとは好きにすればよい.
/// サーバからエラーが返されたときのデリゲート - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { NSLog( @"失敗" ); [m_data release]; m_data = nil; }
通信は、自分の都合だけでは成り立たないので、エラーも考えておきましょう.