web-dev-qa-db-ja.com

完了ブロックなしでNSURLSessionでサーバー応答データを取得する方法

背景画像のアップロードにNSURLSessionを使用しています。アップロードされた画像によると、サーバーから応答があり、それに応じてアプリを変更します。ただし、完了ブロックがないため、アプリがバックグラウンドで画像をアップロードするときにサーバーの応答を取得できません。

NSURLUploadTaskの完了ブロックを使用せずに応答を取得する方法はありますか?

ここに私のコードがあります:

 self.uploadTask = [self.session uploadTaskWithRequest:request fromData:body completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
            NSString *returnString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
            NSLog(@"returnString : %@",returnString);
            NSLog(@"error : %@",error);
        }];
 [self.uploadTask resume];

しかし、このエラーが発生しました。

キャッチされない例外 'NSGenericException'によるアプリの終了、理由: '完了ハンドラーブロックはバックグラウンドセッションではサポートされていません。代わりにデリゲートを使用してください。」

しかし、完了ハンドラを使用できない場合は、サーバーの応答をどのように取得する必要がありますか。デリゲートを使用するというメッセージが表示されますが、サーバーに応答できるデリゲートメソッドが見つかりません。

46
user3648985

いくつかの考え:

バックグラウンドセッションにはデリゲートが必要なため、最初にdelegateを使用してセッションをインスタンス化します。

NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:kSessionIdentifier];
self.session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];

次に、バックグラウンドセッションに追加されたタスクは完了ブロックを使用できないため、完了ハンドラーなしでNSURLSessionUploadTaskをインスタンス化します。また、NSDataではなくファイルURLを使用していることに注意してください。

NSURLSessionTask *task = [self.session uploadTaskWithRequest:request fromFile:fileURL];
[task resume];

第三に、関連するデリゲートメソッドを実装します。少なくとも、次のようになります。

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
    NSMutableData *responseData = self.responsesData[@(dataTask.taskIdentifier)];
    if (!responseData) {
        responseData = [NSMutableData dataWithData:data];
        self.responsesData[@(dataTask.taskIdentifier)] = responseData;
    } else {
        [responseData appendData:data];
    }
}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
    if (error) {
        NSLog(@"%@ failed: %@", task.originalRequest.URL, error);
    }

    NSMutableData *responseData = self.responsesData[@(task.taskIdentifier)];

    if (responseData) {
        // my response is JSON; I don't know what yours is, though this handles both

        NSDictionary *response = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:nil];
        if (response) {
            NSLog(@"response = %@", response);
        } else {
            NSLog(@"responseData = %@", [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding]);
        }

        [self.responsesData removeObjectForKey:@(task.taskIdentifier)];
    } else {
        NSLog(@"responseData is nil");
    }
}

上記は、以前にインスタンス化されたNSMutableDictionaryと呼ばれるresponsesDataを利用していることに注意してください(残念なことに、これらの「タスク」デリゲートメソッドは「セッション」レベルで実行されます)。

最後に、completionHandlerが提供するhandleEventsForBackgroundURLSessionを保存するプロパティを定義する必要があります。

@property (nonatomic, copy) void (^backgroundSessionCompletionHandler)(void);

そして明らかに、アプリデリゲートにhandleEventsForBackgroundURLSessionに応答させて、completionHandlerを保存します。これは、以下のURLSessionDidFinishEventsForBackgroundURLSessionメソッドで使用されます。

- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler {
    // This instantiates the `NSURLSession` and saves the completionHandler. 
    // I happen to be doing this in my session manager, but you can do this any
    // way you want.

    [SessionManager sharedManager].backgroundSessionCompletionHandler = completionHandler;
}

そして、バックグラウンドセッションが終了したときに、NSURLSessionDelegateがメインスレッドでこのハンドラを呼び出すことを確認します。

- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
    if (self.backgroundSessionCompletionHandler) {
        dispatch_async(dispatch_get_main_queue(), ^{
            self.backgroundSessionCompletionHandler();
            self.backgroundSessionCompletionHandler = nil;
        });
    }
}

これは、アップロードの一部がバックグラウンドで完了した場合にのみ呼び出されます。

あなたが見ることができるように、いくつかの可動部分がありますが、それは基本的に伴うものです。

76
Rob