背景画像のアップロードに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'によるアプリの終了、理由: '完了ハンドラーブロックはバックグラウンドセッションではサポートされていません。代わりにデリゲートを使用してください。」
しかし、完了ハンドラを使用できない場合は、サーバーの応答をどのように取得する必要がありますか。デリゲートを使用するというメッセージが表示されますが、サーバーに応答できるデリゲートメソッドが見つかりません。
いくつかの考え:
バックグラウンドセッションにはデリゲートが必要なため、最初に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;
});
}
}
これは、アップロードの一部がバックグラウンドで完了した場合にのみ呼び出されます。
あなたが見ることができるように、いくつかの可動部分がありますが、それは基本的に伴うものです。