J'utilise NSURLSession
pour le téléchargement d'images d'arrière-plan. Et selon l'image téléchargée, mon serveur me donne une réponse et je change mon application en conséquence. Mais je ne peux pas obtenir la réponse de mon serveur lorsque mon application télécharge une image en arrière-plan car il n'y a pas de bloc d'achèvement.
Existe-t-il un moyen d'obtenir une réponse sans utiliser le bloc d'achèvement dans NSURLUploadTask
?
Voici mon code:
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];
Mais j'ai eu cette erreur ..
Arrêt de l'application en raison d'une exception non interceptée "NSGenericException", raison: "Les blocs du gestionnaire d'achèvement ne sont pas pris en charge dans les sessions en arrière-plan. Utilisez plutôt un délégué.
Mais si je ne peux pas utiliser le gestionnaire d'achèvement, comment puis-je obtenir la réponse du serveur. Il dit utiliser délégué mais je ne trouve aucune méthode déléguée qui puisse me donner la réponse du serveur.
Quelques réflexions:
Tout d'abord, instanciez votre session avec un delegate
, car les sessions d'arrière-plan doivent avoir un délégué:
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:kSessionIdentifier];
self.session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
Deuxièmement, instanciez votre NSURLSessionUploadTask
sans gestionnaire d'achèvement, car les tâches ajoutées à une session d'arrière-plan ne peuvent pas utiliser de blocs d'achèvement. Notez également que j'utilise une URL de fichier plutôt qu'un NSData
:
NSURLSessionTask *task = [self.session uploadTaskWithRequest:request fromFile:fileURL];
[task resume];
Troisièmement, implémentez les méthodes de délégué pertinentes. Au minimum, cela pourrait ressembler à:
- (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");
}
}
Notez que ce qui précède tire parti d'un NSMutableDictionary
précédemment instancié responsesData
(car, à mon grand regret, ces méthodes de délégué "tâche" se font au niveau "session").
Enfin, vous voulez vous assurer de définir une propriété pour stocker le completionHandler
fourni par handleEventsForBackgroundURLSession
:
@property (nonatomic, copy) void (^backgroundSessionCompletionHandler)(void);
Et évidemment, demandez à votre délégué d'application de répondre à handleEventsForBackgroundURLSession
, en enregistrant le completionHandler
, qui sera utilisé ci-dessous dans la méthode 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;
}
Et puis assurez-vous que votre NSURLSessionDelegate
appelle ce gestionnaire sur le thread principal lorsque la session d'arrière-plan est terminée:
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
if (self.backgroundSessionCompletionHandler) {
dispatch_async(dispatch_get_main_queue(), ^{
self.backgroundSessionCompletionHandler();
self.backgroundSessionCompletionHandler = nil;
});
}
}
Ceci n'est appelé que si certains des téléchargements se sont terminés en arrière-plan.
Il y a quelques pièces mobiles, comme vous pouvez le voir, mais c'est essentiellement ce que cela implique.