はじめに
iOS 9 より NSURLConnection が DEPRECATED となってしまったので、既存コードを NSURLSession に移行する場合について考察。
Delegateを使っていない場合
非同期処理
非同期処理(+ sendAsynchronousRequest:queue:completionHandler:)の場合は、特に何も考えずにBlocksの中身をそのまま使うことができます。
NetworkManager.m
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
if (connectionError) {
[[NSNotificationCenter defaultCenter] postNotificationName:@"LoadDataFailed" object:nil];
return;
}
NSInteger statusCode = [(NSHTTPURLResponse *)respnse statusCode];
if (statusCode != 200) {
[[NSNotificationCenter defaultCenter] postNotificationName:@"LoadDataFailed" object:nil];
return;
}
[[NSNotificationCenter defaultCenter] postNotificationName:@"LoadDataSuccess" object:data];
}];
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&respnse error:&error];
上のようなコードの場合だと、下のように completionHandler: をほぼそのまま流用することができます。
NetworkManager.m
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// connectionError -> error
if (error) {
[[NSNotificationCenter defaultCenter] postNotificationName:@"LoadDataFailed" object:nil];
return;
}
NSInteger statusCode = [(NSHTTPURLResponse *)respnse statusCode];
if (statusCode != 200) {
[[NSNotificationCenter defaultCenter] postNotificationName:@"LoadDataFailed" object:nil];
return;
}
[[NSNotificationCenter defaultCenter] postNotificationName:@"LoadDataSuccess" object:data];
}];
[task resume];
ただ、別スレッドで処理されているため、Viewの操作をする場合は注意が必要です。
同期処理
同期処理(+ sendSynchronousRequest:returningResponse:error:)の場合は、 NSURLSessionTask が非同期処理であるため、このままでは都合が悪いです。
GCD のディスパッチセマフォという仕組みを利用し、非同期処理を同期処理にすることで対応します。
NetworkManager.m
- (NSString *)connectionSyncRequest:(NSString *)targetUrl
{
NSURL *url = [NSURL URLWithString:targetUrl];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
NSURLResponse *response = nil;
NSError *error = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:&response error:&error];
if (error) {
NSLog(@"%@", error);
return nil;
}
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if (httpResponse.statusCode != 200) {
return nil;
}
NSString *receivedMessage = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
return receivedMessage;
}
上のようなコードの場合では、下のようにディスパッチセマフォを利用して、同期処理にします。
NetworkManager.m
- (NSString *)sessionSyncRequest:(NSString *)targetUrl
{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
NSURL *url = [NSURL URLWithString:targetUrl];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
__block NSString *receivedMessage = nil;
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
NSURLSessionDataTask *task = [session dataTaskWithRequest:urlRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"did finish download.\n%@", response.URL);
if (error) {
NSLog(@"%@", error);
dispatch_semaphore_signal(semaphore);
return;
}
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if (httpResponse.statusCode != 200) {
dispatch_semaphore_signal(semaphore);
return;
}
receivedMessage = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
dispatch_semaphore_signal(semaphore);
}];
[task resume];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
return receivedMessage;
}
Delegateを使っている場合
Delegate を使っている場合も、わりとすんなり移行することができます。
NetworkManager.m
- (void)sessionAsyncRequest:(NSString *)targetUrl
{
NSURL *url = [NSURL URLWithString:targetUrl];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
NSURLSessionDataTask *task = [session dataTaskWithRequest:urlRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"did finish download.\n%@", response.URL);
if (error) {
NSLog(@"%@", error);
[self connection:nil didFailWithError:error];
return;
}
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if (httpResponse.statusCode != 200) {
[self connection:nil didFailWithError:nil];
return;
}
self.receivedData = [NSMutableData dataWithData:data];
[self connectionDidFinishLoading:nil];
}];
[task resume];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// 正常終了時の処理
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
// エラー時の処理
}