2016-12-03 2 views
1

Je veux récupérer des données du serveur avec plusieurs appels à l'intérieur de la boucle. Je passe un paramètre différent à chaque fois. Je sais qu'il est possible de récupérer des données comme, je suis dans le code ci-dessous aller chercher:iOS récupérant des données à partir du serveur à l'intérieur pour la boucle

for (NSDictionary *feedItem in [feed objectForKey:@"content"]) { 
    // url with feedItem data. 
    NSURL *url = .... 
    [UrlMethod GetURL:url success:^(NSDictionary *placeData) { 
     if (placeData) { 
      dispatch_async(dispatch_get_main_queue(), ^{ 
       // adding object to table data source array 
       [dataSourceArray addObject:[placeData objectForKey:@"data"]]; 
       // reloading table view. 
       [self.tableView reloadData]; 

       }); 
      } 
     } failure:^(NSError *error) { 

     }]; 
} 

Le problème est, chaque fois que j'ajouter des données à dataSourceArry, Il n'ajoute pas de manière séquentielle. Il ajoute en fonction de la réponse des appels d'API. S'il vous plaît faites le moi savoir, si ce n'est pas clair.

+0

Il y a beaucoup de publications sur l'OS, avez-vous vérifié celles-ci? –

Répondre

1

Dans votre cas, j'allouera un tableau mutable premier et définir [NSNull null] à chaque position:

NSInteger count = [[feed objectForKey:@"content"] count]; 
NSMutableArray *dataSourceArray = [NSMutableArray arrayWithCapacity:count]; 

for (NSInteger i = 0; i < count; ++i) { 
    [dataSourceArray addObject:[NSNull null]]; 
} 

Ensuite, je voudrais utiliser quelque chose appelé les groupes d'expédition (voir plus ici http://commandshift.co.uk/blog/2014/03/19/using-dispatch-groups-to-wait-for-multiple-web-services/):

__block NSError *apiCallError = nil; // Just to keep track if there was at least one API call error 
NSInteger index = 0; 

// Create the dispatch group 
dispatch_group_t serviceGroup = dispatch_group_create(); 

for (NSDictionary *feedItem in [feed objectForKey:@"content"]) { 

    // Start a new service call 
    dispatch_group_enter(serviceGroup); 

    // url with feedItem data. 
    NSURL *url = ... 

    [UrlMethod GetURL:url success:^(NSDictionary *placeData) { 
     if (placeData) { 
      dispatch_async(dispatch_get_main_queue(), ^{ 
       // Add data to Data Source 
       // index should be the correct one, as the completion block will contain a snapshot of the corresponding value of index 
       dataSourceArray[index] = [placeData objectForKey:@"data"]; 
      } 

      dispatch_group_leave(serviceGroup); 
     } failure:^(NSError *error) { 
      apiCallError = error; 
      dispatch_group_leave(serviceGroup); 
     }]; 

    index++; 
} 

dispatch_group_notify(serviceGroup, dispatch_get_main_queue(),^{ 
    if (apiCallError) { 
     // Make sure the Data Source contains no [NSNull null] anymore 
     [dataSourceArray removeObjectIdenticalTo:[NSNull null]]; 
    } 

    // Reload Table View 
    [self.tableView reloadData]; 
}); 

Espérons que cela fonctionne pour vous.

+0

Enfin quelqu'un a écrit une réponse plus précise :) Je me sentais triste, je comptais je serai le seul à poster le code en utilisant dispatch_group :) Quoi qu'il en soit, excellent travail monsieur :) Pas sûr que ce soit accepté comme réponse, mais 100% précis :) utilisation de dispatch_group :) donc de voter :) –

+0

@SandeepBhandari Merci! – ppalancica

+0

J'utiliserais probablement un NSDictionary avec le numéro d'emplacement comme clé, parce que cela vous permet de vérifier l'état avec [dict count]. – dgatwood

1

Cela pourrait être utile pour vous,

//keep dictionary property which will store responses 
    NSMutableDictionary *storeResponses = [[NSMutableDictionary alloc]init]; 

    //Somewhere outside function keep count or for loop 
    NSInteger count = 0; 

    for (NSDictionary *feedItem in [feed objectForKey:@"content"]) { 
     //Find out index of feddItem 
     NSInteger indexOfFeedItem = [[feed objectForKey:@"content"] indexOfObject:feedItem]; 

     NSString *keyToStoreResponse = [NSString stringWithFormat:@"%d",indexOfFeedItem]; 

     // url with feedItem data. 
     NSURL *url = .... 
     [UrlMethod GetURL:url success:^(NSDictionary *placeData) { 
      if (placeData) { 
       //instead of storing directly to array like below 
       // adding object to table data source array 
       [dataSourceArray addObject:[placeData objectForKey:@"data"]]; 

       //follow this 
       //increase count 
       count++; 
       [storeResponses setObject:[placeData objectForKey:@"data"] forKey:keyToStoreResponse]; 


       // reloading table view. 
       if(count == [feed objectForKey:@"content"].count) 
       { 
        NSMutableArray *keys = [[storeResponses allKeys] mutableCopy]; //or AllKeys 
        //sort this array using sort descriptor 
        //after sorting "keys" 

        for (NSString *key in keys) 
        { 
         //add them serially 

         [dataSourceArray addObject:[storeResponses objectForKey:key]]; 
        } 

        dispatch_async(dispatch_get_main_queue(), ^{ 


         [self.tableView reloadData]; 



        }); 
       } 

      } 
     } failure:^(NSError *error) { 

     }]; 
    } 

Edit: La réponse que j'ai donné est directement écrit ici, vous pourriez faire face erreurs de compilation en fait d'exécuter ce code

+0

S'il vous plaît ne pas simplement poster un tas de code sans explication. Une bonne réponse explique le problème et explique comment la réponse résout le problème. – rmaddy

+0

@rmaddy merci pour la remarque, mais les pluies sait quel est le problème et j'ai ajouté beaucoup de commentaires dans le code, pour aider à comprendre la solution proposée.Si vous vous sentez toujours le besoin de modifier la réponse, vous pouvez aller de l'avant et modifier ma réponse ou laissez-moi savoir ce que vous ressentez et je vais mettre à jour selon cela. – sanman

0

En effet, vous appelez les services Web de manière asynchrone, donc ce n'est pas une garantie que vous répondez en séquence lorsque vous avez fait une demande!

solutions maintenant pour que:

  • Vous devriez écrire votre api comme il est donner toutes les données à la fois. Donc, Vous n'avez pas besoin de faire de nombreux appels réseau et il va améliorer les performances aussi!
  • Deuxième chose que vous pouvez faire type de fonction récursif, je veux dire faire une autre demande de completion handler du précédent. Dans ce cas, une fois que vous avez reçu une réponse, seule une autre requête sera initialisée mais dans ce cas, vous devrez faire des compromis avec la performance !! Donc la première solution est meilleure selon moi!
  • Une autre chose que vous pouvez trier votre tableau après que vous obtenez toutes les réponses et vous pouvez recharger votre tableView
+1

Je voudrais aussi vous suggérer d'aller avec cette approche. –

+0

ouais ....... :) @Dev_Tandel – Lion

0

Effectuez les opérations suivantes: -

for (NSDictionary *feedItem in [feed objectForKey:@"content"]) { 
     // url with feedItem data. 
     NSURL *url = .... 
     [UrlMethod GetURL:url success:^(NSDictionary *placeData) { 
      if (placeData) { 

        // adding object to table data source array 
        [dataSourceArray addObject:[placeData objectForKey:@"data"]]; 
        // reloading table view. 
dispatch_sync(dispatch_get_main_queue(), ^{ 
        [self.tableView reloadData]; 
    }); 
        }); 

      } failure:^(NSError *error) { 

      }]; 
    } 
+1

Les opérations d'interface utilisateur doivent toujours être sur le thread principal. – ppalancica

+0

@ppalancica Merci !! –

+0

Comment cette réponse est-elle censée résoudre le problème? C'est en fait pire. – rmaddy

0

Ne pas recharger votre table chaque fois la boucle. Une fois la récupération des données terminée, effectuez un tri sur datasourcearray pour obtenir le résultat souhaité, puis rechargez la table.