Sukhpreet
Sukhpreet

Reputation: 933

iOS fetching data from server inside for loop

I want to fetch data from server with multiple calls inside for loop. I'm passing different parameter each time. I know it is possible to fetch data like, I'm fetching in code below :

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) {

       }];
}

The problem is, Whenever I add data to dataSourceArry, It is not adding sequentially. It is adding according to response of API calls. Please let me know, If it is not clear.

Upvotes: 1

Views: 1118

Answers (5)

ppalancica
ppalancica

Reputation: 4277

In your case, I would allocate a mutable array first and set [NSNull null] at each position:

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

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

Then, I would use something called dispatch groups (see more here 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];
});

Hope it works for you.

Upvotes: 2

Tanvi Jain
Tanvi Jain

Reputation: 937

Try the following :-

    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) {

           }];
    }

Upvotes: 0

Sanman
Sanman

Reputation: 1158

This might be of help for you,

 //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 : The answer I have given is directly written here,you might face compilation errors while actually running this code

Upvotes: 1

Ketan Parmar
Ketan Parmar

Reputation: 27428

This is because you're calling web-services asynchronously so it's not give guarantee that it's give response in sequence as you have made request!

Now solutions for that :

  • You should write your api like it's give all data at a time. So, You not need to make many network call and it will improve performance also!
  • Second thing you can make recursive kind of function, I mean make another request from completion handler of previous one. In this case once you got response then only another request will be initialize but in this case you will have to compromise with performance!! So first solution is better according to me!
  • Another thing you can sort your array after you get all the responses and then you can reload your tableView

Upvotes: 0

Krishna Datt Shukla
Krishna Datt Shukla

Reputation: 997

Don't reload your table each time in the loop. After the loop finishes fetching data , do a sorting on your datasourcearray to get the desired result and then reload table.

Upvotes: 0

Related Questions