NSURLConnection for multiple views not receiving data asynchronously

Discussion in 'iOS Programming' started by ashwinr87, Nov 2, 2011.

  1. ashwinr87, Nov 2, 2011
    Last edited: Nov 2, 2011

    ashwinr87 macrumors member

    Joined:
    Mar 9, 2011
    #1
    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?
     
  2. robbieduncan Moderator emeritus

    robbieduncan

    Joined:
    Jul 24, 2002
    Location:
    London
    #2
    Where is this code? In what class? I suspect you have one instance and you are calling callMedicationWebService 6 times on the same instance...
     
  3. ashwinr87 thread starter macrumors member

    Joined:
    Mar 9, 2011
    #3
    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...

     
  4. robbieduncan Moderator emeritus

    robbieduncan

    Joined:
    Jul 24, 2002
    Location:
    London
    #4
    Have you used logging in the callMedicationWebService to check when you are actually calling that method?
     
  5. ashwinr87 thread starter macrumors member

    Joined:
    Mar 9, 2011
    #5
    yes.. I see that the request goes out at the same time for all the views...

     
  6. jiminaus macrumors 65816

    jiminaus

    Joined:
    Dec 16, 2010
    Location:
    Sydney
    #6
    Implement the connection completion delegate method connection:didFailWithError:. Is it called? If so, what is the error?
     
  7. Sykte macrumors regular

    Joined:
    Aug 26, 2010
    #7
    Who's the delegate for the connection?
     
  8. ashwinr87 thread starter macrumors member

    Joined:
    Mar 9, 2011
    #8
    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...

     
  9. jiminaus, Nov 2, 2011
    Last edited: Nov 2, 2011

    jiminaus macrumors 65816

    jiminaus

    Joined:
    Dec 16, 2010
    Location:
    Sydney
    #9
    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
                            }
                        );
                }
            );
    }
    
     
  10. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #10
    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.
     
  11. North Bronson macrumors 6502

    Joined:
    Oct 31, 2007
    Location:
    San José
    #11
    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.
     
  12. Simon86 macrumors member

    Joined:
    Sep 27, 2011
    #12
    To expand on what has already been said: asynchronous != concurrent ;)
     
  13. ashwinr87 thread starter macrumors member

    Joined:
    Mar 9, 2011
    #13
    yeah.. that is exactly the problem I am facing... I will try and use GCD to see if it is working...
    thanks...



    ----------

    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..



    ----------

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

     
  14. Sykte, Nov 3, 2011
    Last edited: Nov 3, 2011

    Sykte macrumors regular

    Joined:
    Aug 26, 2010
    #14

    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
     

Share This Page