LoginSignup
23
17

More than 5 years have passed since last update.

NSURLConnection から NSURLSession への移行例

Last updated at Posted at 2016-07-26

はじめに

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

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
17