web-dev-qa-db-fra.com

Comment faire une demande par lot avec AFNetworking 2?

Je réécris donc une application pour iOS 7 avec AFNetworking 2.0 et je rencontre le problème d'envoyer un lot de demandes à la fois et de suivre leur progression. Dans l'ancien AFNetworking, il y avait la méthode enqueueBatchOfHTTPRequestOperations:progressBlock:completionBlock: Sur AFHTTPClient, elle est clairement refactorisée et je suis un peu confus sur la façon de mettre en file d'attente plusieurs demandes.

J'ai créé une sous-classe de AFHTTPSessionManager et j'utilise les méthodes POST:... Et GET:... Pour communiquer avec le serveur. Mais je ne trouve rien dans le code et/ou les documents pour mettre en file d'attente plusieurs demandes à la fois comme avec l'ancien AFHTTPClient.

La seule chose que je peux trouver est la méthode batchOfRequestOperations:progressBlock:completionBlock: Non documentée sur AFURLConnectionOperation, mais qui ressemble à la manière iOS 6 de le faire.

De toute évidence, il me manque quelque chose dans le nouveau concept NSURLSession que je devrais utiliser pour traiter des demandes par lots ou rechercher une nouvelle fonctionnalité AFNetworking. J'espère que quelqu'un pourra m'aider sur la bonne voie ici!

tl; dr: Comment envoyer un lot de requêtes avec ma sous-classe AFHTTPSessionManager?

49
Mac_Cain13

Merci Sendoa pour le lien vers le problème GitHub où Mattt explique pourquoi cette fonctionnalité ne fonctionne plus. Il y a une raison claire pour laquelle cela n'est pas possible avec la nouvelle structure NSURLSession; Les tâches ne sont tout simplement pas des opérations, donc l'ancienne façon d'utiliser les dépendances ou les lots d'opérations ne fonctionnera pas.

J'ai créé cette solution à l'aide d'un dispatch_group qui permet de batchter des requêtes en utilisant NSURLSession, voici le code (pseudo-):

// Create a dispatch group
dispatch_group_t group = dispatch_group_create();

for (int i = 0; i < 10; i++) {
    // Enter the group for each request we create
    dispatch_group_enter(group);

    // Fire the request
    [self GET:@"endpoint.json"
       parameters:nil
          success:^(NSURLSessionDataTask *task, id responseObject) {
                  // Leave the group as soon as the request succeeded
                  dispatch_group_leave(group);
          }
      failure:^(NSURLSessionDataTask *task, NSError *error) {
                  // Leave the group as soon as the request failed
                  dispatch_group_leave(group);
              }];
}

// Here we wait for all the requests to finish
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    // Do whatever you need to do when all requests are finished
});

Je veux regarder écrire quelque chose qui rend cela plus facile à faire et discuter avec Matt si c'est quelque chose (une fois bien implémenté) qui pourrait être fusionné dans AFNetworking. À mon avis, ce serait formidable de faire quelque chose comme ça avec la bibliothèque elle-même. Mais je dois vérifier quand j'ai du temps libre pour ça.

85
Mac_Cain13

Pour request qui peut être post ou get, vous pouvez utiliser AFNetworking 2.0 pour une opération par lots car vous devez d'abord créer une opération comme celle-ci:

//Request 1
NSString *strURL = [NSString stringWithFormat:@"your url here"];
NSLog(@"scheduleurl : %@",strURL);
NSDictionary *dictParameters = your parameters here
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] requestWithMethod:@"POST" URLString:strURL parameters:dictParameters error: nil];

AFHTTPRequestOperation *operationOne = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operationOne = [AFHTTPResponseSerializer serializer];

[operationOne setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
     //do something on completion
} 
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
     NSLog(@"%@",[error description]);
}];

//Request 2
NSString *strURL1 = [NSString stringWithFormat:@"your url here"];
NSLog(@"scheduleurl : %@",strURL);
NSDictionary *dictParameters1 = your parameters here
NSMutableURLRequest *request1 = [[AFHTTPRequestSerializer serializer] requestWithMethod:@"POST" URLString:strURL1 parameters:dictParameters1 error: nil];

AFHTTPRequestOperation *operationTwo = [[AFHTTPRequestOperation alloc] initWithRequest:request1];
operationTwo = [AFHTTPResponseSerializer serializer];

[operationTwo setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
     //do something on completion
} 
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
     NSLog(@"%@",[error description]);
}];

//Request more here if any

Effectuez maintenant une opération par lots comme ceci:

//Batch operation
//Add all operation here 
NSArray *operations = [AFURLConnectionOperation batchOfRequestOperations:@[operationOne,operationTwo] progressBlock:^(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations)
{
    NSLog(@"%i of %i complete",numberOfFinishedOperations,totalNumberOfOperations);
    //set progress here
    yourProgressView.progress = (float)numberOfFinishedOperations/(float)totalNumberOfOperations;

} completionBlock:^(NSArray *operations) 
{
    NSLog(@"All operations in batch complete");
}];

[[NSOperationQueue mainQueue] addOperations:operations waitUntilFinished:NO];
3
Paresh Navadiya

Juste mettre à jour le fil ... J'ai eu le même problème et après quelques recherches, j'ai trouvé de bonnes solutions, mais j'ai décidé de m'en tenir à celui-ci:

J'utilise le projet appelé Bolts . Donc, pour le même exemple ci-dessus publié par @ Mac_Cain13, ce serait:

[[BFTask taskWithResult:nil] continueWithBlock:^id(BFTask *task) {
    BFTask *task = [BFTask taskWithResult:nil];
    for (int i = 0; i < 10; i++) {
        task = [task continueWithBlock:^id(BFTask *task) {
            return [self executeEndPointAsync];
        }];
    }
    return task;
}] continueWithBlock:^id(BFTask *task) {
    // Everything was executed.
    return nil;
}];;

- (BFTask *) executeEndPointAsync {
    BFTaskCompletionSource *task = [BFTaskCompletionSource taskCompletionSource];
    [self GET:@"endpoint.json" parameters:nil
      success:^(NSURLSessionDataTask *task, id responseObject) {
        [task setResult:responseObject];
      }
      failure:^(NSURLSessionDataTask *task, NSError *error) {
        [task setError:error];
      }];
    }];
    return task.task;
}

Fondamentalement, il empile toutes les tâches, attend et déballe jusqu'à ce qu'il n'y ait plus de tâches, et une fois que tout est terminé, le dernier bloc d'achèvement est exécuté.

Un autre projet qui fait la même chose est RXPromise, mais pour moi le code en Bolts était plus clair.

3
jairobjunior

Sur AFNetworking 2.0, AFHTTPClient a été divisé sur AFHTTPRequestOperationManager et AFHTTPSessionManager, donc vous pourriez probablement commencer par le premier, qui a la propriété operationQueue.

1
Ruenzuo

Actuellement, les tâches NSURLSession ne conviennent pas au même type de modèle que les opérations de demande utilisées. Voir la réponse de Mattt Thompson ici concernant ce problème.

Réponse directe: si vous avez besoin de dépendances ou de lots, vous devrez toujours utiliser les opérations de demande.

1
Sendoa