Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

ashwinr87

macrumors member
Original poster
Mar 9, 2011
81
0
In my iPAD application, I have 6 UITableViews. To get data for each of the tableview, I call a Webservice using NSURLConnection and parse the xml I get back from the Webservice and store data into the database.

Since I have 6 UITableView, I send the Webservice request for each of the views at the same time. However, the problem that I am facing is that, for my app to receive data from the Webservice on the
Code:
 -(void) connection:(NSURLConnection *) connection didReceiveData:(NSData *) data
for 1 table view keeps depending upon the database operations performed by the parsers of the other tableviews.

For example, the webservice request for tableview's A, B, C, D are all sent at the same time. if I get back the data on the
Code:
-(void) connection:(NSURLConnection *) connection didReceiveData:(NSData *) data
function, until the xml received is parsed and saved to my database, I am not getting the response back for the other tableviews.

I am unable to figure out what I am doing wrong. I know NSURLConnection is asynchronous but the response I am getting does not seem so.

Here is my code -

For sending the Webservice request -

Code:
- (void) callMedicationWebService
{
    . \\ declaring stuff needed to point to Webservice
    .
    .

    conn = [[NSURLConnection alloc] initWithRequest:req delegate:self];
    if (conn) 
    {
        webData = [[NSMutableData data] retain];
    }
}

-(void) connection:(NSURLConnection *) connection 
didReceiveResponse:(NSURLResponse *) response 
{
    [webData setLength: 0];
}


-(void) connection:(NSURLConnection *) connection 
    didReceiveData:(NSData *) data 
{
    [webData appendData:data];

    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    [formatter setDateFormat:@"HH:mm:ss"];
    NSString *alertMessage = [formatter stringFromDate:[NSDate date]];
    [formatter release];

    NSLog(@"got data back from WS %@", alertMessage);
}

-(void) connectionDidFinishLoading:(NSURLConnection *) connection 
{
    [connection release];

    // Parse xml
    NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithData:[CommonHelper decodeHTMLCharactorsFromString:webData]];

    TableAHandler *handler = [[TableAHandler alloc] init];
    [handler initTableAHandler];
    [xmlParser setDelegate:handler];
    [xmlParser setShouldResolveExternalEntities:YES];
    [xmlParser setShouldProcessNamespaces:YES];

    BOOL success = [xmlParser parse];
   }

Would someone be able to help me what I am doing wrong?
 
Last edited:

robbieduncan

Moderator emeritus
Jul 24, 2002
25,611
893
Harrogate
Where is this code? In what class? I suspect you have one instance and you are calling callMedicationWebService 6 times on the same instance...
 

ashwinr87

macrumors member
Original poster
Mar 9, 2011
81
0
this code is present in each an every viewcontroller's .m file. for each of the viewcontroller, I have different functions to call the webservice...
So for view A, it would be like callAWebservice and for view B, it would be callBWebservice and so on...
I call different webservices for each of the tableviews...

Where is this code? In what class? I suspect you have one instance and you are calling callMedicationWebService 6 times on the same instance...
 

ashwinr87

macrumors member
Original poster
Mar 9, 2011
81
0
it does not go to connection:didFailWithError..
The operation of the NSURLConnection happens properly and I am able to parse and save data..

The problem I am facing is that, when 6 NSURLConnections are called at the same time, the connection didReceiveData function does not happen asynchronously.. i.e. once I the connection:didReceiveData is got from 1 webservice, it goes on to parse and save the data in the database, but the connection:didReceiveData for the other views dont get called even though the webservice does return data...

yes.. I see that the request goes out at the same time for all the views...
 

jiminaus

macrumors 65816
Dec 16, 2010
1,449
1
Sydney
it does not go to connection:didFailWithError..
The operation of the NSURLConnection happens properly and I am able to parse and save data..

The problem I am facing is that, when 6 NSURLConnections are called at the same time, the connection didReceiveData function does not happen asynchronously.. i.e. once I the connection:didReceiveData is got from 1 webservice, it goes on to parse and save the data in the database, but the connection:didReceiveData for the other views dont get called even though the webservice does return data...

Ah, I think I'm understanding now. You are getting the callback to connection:didReceiveData: for all 6 NSURLConnections, but they're happening sequentially?

That makes sense. You're starting all 6 NSURLConnection objects on the same thread (the main thread). The callbacks are guaranteed to happen on the same thread on which the NSURLConnection object was initialised. So while one of the callbacks is executing on that thread, the execution of all the other callbacks are queued.

If you want the callbacks to execute concurrently, you'll need to initialise the 6 NSURLConnection objects on 6 different threads.

Or use GCD to dispatch the real work of the callback. So instead of doing the actual work in the callback method, you just add a block to the global concurrent dispatch queue. For example, your connectionDidFinishLoading: becomes:

Code:
-(void) connectionDidFinishLoading:(NSURLConnection *) connection 
{
    [connection release];

    dispatch_queue_t globalConcurrentQueue = 
        dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_async(
            globalConcurrentQueue,
            ^{
                // Parse XML
                // ...
                dispatch_queue_t mainQueue = dispatch_get_main_queue();
                dispatch_async(
                        mainQueue,
                        ^{
                            // Update UI
                        }
                    );
            }
        );
}
 
Last edited:

chown33

Moderator
Staff member
Aug 9, 2009
10,750
8,422
A sea of green
If you want the callbacks to execute concurrently, you'll need to initialise the 6 NSURLConnection objects on 6 different threads.

Or use GCD to dispatch the real work of the callback. So instead of doing the actual work in the callback method, you wrap the code in a NSOperation.
This.

The key point is to not block inside any of the callback methods for any unreasonable amount of time. XML parsing alone probably isn't causing significant blocking, but I could easily see how a large number of database operations might.

At the very least, it should be worthwhile profiling the code, so you know where the time is really being spent. You need a finer level of detail than just "in the received-data callback". Once you have a more detailed profile, you can do something to minimize the time that's spent blocked. But without profiling, you're just guessing where the real time-killer is. Guessing is frequently wrong in these situations, even when done by experienced developers.
 

North Bronson

macrumors 6502
Oct 31, 2007
395
1
San José
If you are building only for the new OS, there is new API for actually providing an NSOperationQueue that the connection should use to run your delegate methods. I have not used this yet, but it might be a good place to look.

Another tip is that if you are having trouble waiting for delegate methods, set the NSURLConnection to run on the current run loop with NSRunLoopCommonModes.
 

ashwinr87

macrumors member
Original poster
Mar 9, 2011
81
0
yeah.. that is exactly the problem I am facing... I will try and use GCD to see if it is working...
thanks...

Ah, I think I'm understanding now. You are getting the callback to connection:didReceiveData: for all 6 NSURLConnections, but they're happening sequentially?

That makes sense. You're starting all 6 NSURLConnection objects on the same thread (the main thread). The callbacks are guaranteed to happen on the same thread on which the NSURLConnection object was initialised. So while one of the callbacks is executing on that thread, the execution of all the other callbacks are queued.

If you want the callbacks to execute concurrently, you'll need to initialise the 6 NSURLConnection objects on 6 different threads.

Or use GCD to dispatch the real work of the callback. So instead of doing the actual work in the callback method, you just add a block to the global concurrent dispatch queue. For example, your connectionDidFinishLoading: becomes:

Code:
-(void) connectionDidFinishLoading:(NSURLConnection *) connection 
{
    [connection release];

    dispatch_queue_t globalConcurrentQueue = 
        dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_async(
            globalConcurrentQueue,
            ^{
                // Parse XML
                // ...
                dispatch_queue_t mainQueue = dispatch_get_main_queue();
                dispatch_async(
                        mainQueue,
                        ^{
                            // Update UI
                        }
                    );
            }
        );
}


----------

thank you.. yes I did use the time profiler instrument to find out what is taking time, but since I am kind of a newbie, I was not able to really understand the output tree it was giving..

This.

The key point is to not block inside any of the callback methods for any unreasonable amount of time. XML parsing alone probably isn't causing significant blocking, but I could easily see how a large number of database operations might.

At the very least, it should be worthwhile profiling the code, so you know where the time is really being spent. You need a finer level of detail than just "in the received-data callback". Once you have a more detailed profile, you can do something to minimize the time that's spent blocked. But without profiling, you're just guessing where the real time-killer is. Guessing is frequently wrong in these situations, even when done by experienced developers.


----------

ok.. i will try that... thanks for the reply..

If you are building only for the new OS, there is new API for actually providing an NSOperationQueue that the connection should use to run your delegate methods. I have not used this yet, but it might be a good place to look.

Another tip is that if you are having trouble waiting for delegate methods, set the NSURLConnection to run on the current run loop with NSRunLoopCommonModes.
 

Sykte

macrumors regular
Aug 26, 2010
223
0
Code:
-(void) connectionDidFinishLoading:(NSURLConnection *) connection 
{
    dispatch_queue_t globalConcurrentQueue = 
        dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

}


Be careful with global queues, you are unable to suspend\resume them. If you think you will have the need, create your own.

Code:
dispatch_queue_create("com.apple.myapp.blah.blah", DISPATCH_QUEUE_CONCURRENT);

Considering conccurent queues are only supported in iOS5 and 10.7, you might as well use NSURLConnections class method.

Code:
+(void)sendAsynchronousRequest:(NSURLRequest *)request
                          queue:(NSOperationQueue*) queue
              completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))

I would have to agree with chown33, it's better to profile now then change code.

Good luck
 
Last edited:
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.