Search code examples
iosconcurrencynsurlconnection

Make sure async methods execute in same order as they were called


Let's say I have 3 async methods which all do the same thing: send some data to a server and add the response data to an array (all of them add to the same array).

I call these methods at the same time, but they send different amounts of data to the server.

If the first method sends more data to the server than the third method, the third method will get the response back sooner, thus add the data to the array sooner. The response data consits of coordinates, so the order in which they are in the array matters.

How can I make sure, that even if the third method gets the repsonse sooner than the first or the second one, it won't add the data to the array before the previous methods did? Thus, preserving the order of the coordinates in the array.

The methods are NSURLConnections and they all send an async request.

EDIT: Here is the working code:

 //Array of snapped points from the JSON
            NSArray *snappedPoints = [result objectForKey:@"snappedPoints"];

            NSMutableArray *locationsArray = [[NSMutableArray alloc] init];

            //Loop through the snapped points array and add each coordinate to a temporary array
            for (int i = 0; i<[snappedPoints count]; i++) {
                NSDictionary *location = [[snappedPoints objectAtIndex:i] objectForKey:@"location"];

                double latitude = [[location objectForKey:@"latitude"] doubleValue];
                double longitude = [[location objectForKey:@"longitude"] doubleValue];

                CLLocation *loc = [[CLLocation alloc] initWithLatitude:latitude longitude:longitude];

                [locationsArray addObject:loc];

            }


            //Add these temporary location arrays to the dictionary with the key as the request number
            [tempLocationDictionary setObject:locationsArray forKey:[NSString stringWithFormat:@"%i",requestNumber]];

            //If all requests have completed get the location arrays from the dicitonary in the same order as the request were made
            if([tempLocationDictionary count] == numberOfRequests){

                //Just a path because I am drawing these coordinates on a map later
                GMSMutablePath *path = [GMSMutablePath path];

                //Loop through the dictionary and get the location arrays in the right order
                for (int i = 0; i<[tempLocationDictionary count]; i++) {
                    //Create a dummy array
                    NSArray *array = [tempLocationDictionary objectForKey:[NSString stringWithFormat:@"%i",i+1]];

                    //Get the coordinates from the array which we just got from the dictionary
                    for (CLLocation *location in array) {
                        [path addCoordinate:location.coordinate];

                    }
                }

Solution

  • One way would be to replace NSURLConnection with NSURLSession and its related classes. It's newer and generally a superior choice. For your specific situation, it's possible to use a custom NSURLSessionConfiguration where you would set HTTPMaximumConnectionsPerHost to 1. That way the connections would be forced to run in order, solving your problem.

    Of course, not having simultaneous connections might slow things down. In that case you'll have to accumulate the data temporarily without adding it to your array and only update the array when all the connections are complete. There are various ways you might do this depending on exactly what data comes back from the server.

    One relatively simple way: If you know you'll always have three connections, use an NSMutableDictionary with integer NSNumber objects as the keys. After each connection you'd do something like

    mutableDictionary[@1] = // your server data here
    

    Use @2 or @3 for the other connections. Every time you add data, check to see if you have results for all three, and if so, add everything to your array. There are lots of other ways around this, the key is to have some kind of temporary structure where you can (a) accumulate data until all the connections are complete, and (b) keep track of which data came from which connection, either simply by number, or by URL, or by some other unique data that the server provides.