Network
iOS

NSURLConnection から NSURLSession への移行例

More than 1 year has passed since last update.


はじめに

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
{
// エラー時の処理
}