0

I want to perform a task that must run in the back ground. The task involves parsing the response of Web service. As it takes some time, i wanted this to run on back ground. Below is the code i tried to perform background task.

//**When a button is tapped this method will be called.**
dispatch_queue_t myQueue=dispatch_queue_create("My Queue",  NULL);

    dispatch_async(myQueue, ^{

        [self getDataPetioleGraph];//**This will parse the webservice response and the output will be stored in a array for populating on tableview**


        dispatch_async(dispatch_get_main_queue(), ^{


            [tableViewObj reloadData];
        });

    });

-(void)getDataPetioleGraph{

NSString *soapContent=[NSString stringWithFormat:@"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
                       "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"
                       "<soap:Body>"
                       "<GetDataPetioleGraph xmlns=\"http://tempuri.org/\">"
                       "<Account_Number>%@</Account_Number>"
                       "</GetDataPetioleGraph>"
                       "</soap:Body>"
                       "</soap:Envelope>",@"38003"];
NSMutableURLRequest *request=[[[NSMutableURLRequest alloc]initWithURL:[NSURL URLWithString:@"web service link is here"]] autorelease];
NSString *contentLength=[NSString stringWithFormat:@"%d",[soapContent length]];
[request addValue:@"text/xml" forHTTPHeaderField:@"Content-Type"];
[request addValue:@"http://tempuri.org/GetDataPetioleGraph" forHTTPHeaderField:@"SOAPAction"];
[request addValue:contentLength forHTTPHeaderField:@"Content-Length"];
[request setHTTPMethod:@"POST"];
[request setHTTPBody:[soapContent dataUsingEncoding:NSUTF8StringEncoding]];    
getDPGConnection=[[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
[getDPGConnection start];

But when i tap the button that calls the above function, nothing happens. I have tried the other answers but still i am not getting the solution. can anyone tell me where i went wrong. Thanks in advance.

surendher
  • 1,374
  • 3
  • 26
  • 52
  • Can you show the code for getDataPetioleGraph? – Peter Warbo Jul 17 '13 at 07:37
  • have you set a breakpoint in `getDataPetioleGraph`? confirm it is hit, then step through execution – justin Jul 17 '13 at 07:39
  • Could you try to use `dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)` instead of your queues? – Maen Jul 17 '13 at 07:39
  • @PeterWarbo I added code for the petiole graph. Please check it. – surendher Jul 17 '13 at 07:40
  • @justin Ya i did and it is coming – surendher Jul 17 '13 at 07:41
  • First of all make sure the method is called second put a breakpoint inside myQueue block and see if self is not null, your reference may be lost inside the block and we'll go from there. And since you are using GCD i would take Bigood's advice and use a dispatch_get_global_queue. – Radu Jul 17 '13 at 07:42
  • @surendher ok - and does your url request complete successfully? you should be more specific about what is going wrong because there are many steps and "nothing happens" doesn't give us much of a clue as to which step is failing. – justin Jul 17 '13 at 07:53
  • You don't need a background thread for an asynchronous request. Basically what you are doing is creating a background thread then sending an asynchronous request which is creating another background thread to get the data you're asking for and it will return its results via a delegate method which you need to implement. This can all be done via the main thread in this case so you are overcomplicating it. The problem with your snippet of code above is that you are expecting the request to return immediately but because it is asynchronous it won't. – Rog Jul 17 '13 at 08:28

6 Answers6

1

Since the NSURLConnection you are using is asynchronous [self getDataPetioleGraph]; will return immediately and then your UITableView will reload the data with no fetched results from the webservice.

So one way is to implement the delegate method - (void)connectionDidFinishLoading:(NSURLConnection *)connection and wait there until all data has been received from the webservice then you can start parsing the JSON data in a background thread.

So something like this:

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {

     // Now your download has finished asynchronously, you can start to process the JSON
     dispatch_async(dispatch_get_global_queue(0, 0), ^{

        BOOL isJSONProcessed = [self processJSON];

        if (isJSONProcessed) {

            dispatch_async(dispatch_get_main_queue(), ^{

                [self.yourTableView reloadData];
            });
});
Peter Warbo
  • 11,136
  • 14
  • 98
  • 193
0

You have several ways.

I like using NSOperationQueues since they provide a completion block in which you can call the UI functions once youve recieved the data.

Example:

NSBlockOperation * op = [NSBlockOperation blockOperationWithBlock:^{
    [self getDataPetioleGraph];
}];

NSOperationQueue * opQueue = [[NSOperationQueue new] autorelease];
[op setQueuePriority:NSOperationQueuePriorityNormal];
[opQueue addOperation:op];
//Once we receive the neccesary data we populate the views with it in in the main thread
op.completionBlock = ^{
    dispatch_async(dispatch_get_main_queue(), ^{
       [tableViewObj reloadData];
    });
};
pdrcabrod
  • 1,467
  • 1
  • 14
  • 22
0

There are many ways for this like including NSThread, NSOperation and as one you used. Here's the simple example:

dispatch_queue_t jsonParsingQueue = dispatch_queue_create("parsingQueue", NULL);

// execute a task on that queue asynchronously
dispatch_async(jsonParsingQueue, ^{
    [self doYourJsonParsingAndReadingTask];
    //As once this done and you need to call any code in main thread
    dispatch_async(dispatch_get_main_queue(), ^{
        [self.viewController updateThingsWithNewData];
    });
});

// release the dispatch queue if ARC not enable
dispatch_release(jsonParsingQueue);
Buntylm
  • 7,345
  • 1
  • 31
  • 51
0

You don't need a background thread for an asynchronous request. Basically what you are doing is creating a background thread then sending an asynchronous request which is creating another background thread to get the data you're asking for and it will return its results via a delegate method which you need to implement.

The problem with your snippet of code above is that you are expecting the request to return immediately but because it is asynchronous it won't.

This can all be done via the main thread in this case so you are overcomplicating it.

Rog
  • 18,602
  • 6
  • 76
  • 97
  • I want to call two web services(Have to parse also) concurrently. That is the reason i want to use GCD or any other multithreading tasks – surendher Jul 17 '13 at 10:12
0

The reason why your delegates (if you implemented them at all) are not invoked, is that you start the connection on an unspecified background thread which is associated by your queue myQueue. This unspecified thread will not have a run loop. The actual thread can even change for different blocks dispatched to that queue.

A NSURLConnection requires to be scheduled on

a) a dedicated thread which has a run loop. Or

b) a NSOperationQueue.

Unless you specify explicitly where a connection object will be scheduled, the connection's delegate methods will be executed on the same thread where the start method has been send to the connection. When this thread has no run loop, your delegates won't be invoked.

Case a) can be implemented as follows:

Scheduling a NSURLConnection on a Run Loop

First, ensure you are running on a dedicated thread and ensure this thread has a run loop. This can be the main thread or any other thread where you took care to add a run loop. (The latter is more tricky than desired, and I don't explain it here for brevity).

Create the NSURLConnection object, ensuring it will be not immediately started:

connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO];

Then schedule the connection on the current thread/run loop and start it:

[self.connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
[self.connection start];

Notice: this way, the current thread's run loop will be used to schedule the delegates. There is no possibility to specify another thread than the current thread!

Now using a NSOperationQueue where the delegates will be executed:

Scheduling a NSURLConnection on a NSOperationQueue

This is actually simpler. You just need a NSOperationQueue instance, and then start the connection as follows:

First, create your NSOperationQueue instance where the delegates will be scheduled:

self.queue = ...;  

Create the NSURLConnection object, ensuring it will be not immediately started:

connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO];

Schedule and start the connection:

[self.connection setDelegateQueue:self.queue];
[self.connection start];

The above will solve one part of your problem. It doesn't explain how you implement the delegates, where you save the response data, and how you dispatch the lengthy parse operation onto a processing queue.

Here is a sample on Gist which implements a GET request using a dedicated class SimpleGetHTTPRequest. You may find this helpful to implement your POST request.

CouchDeveloper
  • 18,174
  • 3
  • 45
  • 67
-2

NSOperationQueue should work I Think.

Poorva
  • 130
  • 6
  • NSOperationQueue is an higher level function than dispatch_async(), but is basically the same. See the [first answer of this question](http://stackoverflow.com/questions/11676629/ios-dispatch-async-vs-nsoperationqueue) – Maen Jul 17 '13 at 07:42