0

I am working with an app which is todo list organizer, where user adds notes. I am using coredata DB to store the notes. As I am providing sync feature, I am parsing JSON data to server, and also getting JSON data from server.
I am using NSURLConnection API and its delegate functions

- (void)pushData
{
      loop through the notes array and send notes 1 by one

       [[request setValue:@"application/json;charset=utf-8" forHTTPHeaderField:@"Content-Type"];

            [request setHTTPMethod:@"POST"];
            [request setHTTPBody:jsonData];

            m_dataPush = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];

            [m_dataPush start];
}


- (void)connectionDidFinishLoading:(NSURLConnection *)connection 
{
      Process response from server, save to core DB
      and again pushData if any modified and again process the response
}

I call this API, on appEnterBackground and appBecomeActive, because, I want the data to updated on multiple devices.

The problems, which I am facing is that

1) When the notes are more, app is getting stuck, when we exit and open the app and start adding notes.

2) I tried using GCD, but then my NSURLConnection doesnot send me any response

Regards Ranjit

Ranjit
  • 4,576
  • 11
  • 62
  • 121
  • Your 1 and 2 are not very clear. Try to revise... – user523234 Jan 31 '15 at 10:38
  • what is that not clear? I will explain – Ranjit Jan 31 '15 at 10:44
  • Define what is background? Did you want to send/receive while your app is not in foreground (not active)? – user523234 Jan 31 '15 at 11:18
  • If you don't download a very large amount of data or you do intensive operation right after it, keeping an ASYNC download on the main thread is ok. So I would check somewhere else. Maybe is the process that you do after download, In this case you can use GCD on that particular process. Delegation on background thread is a little bit tricky, because if you don't keep the background thread alive, the thread is killed once the first method is called. The method suggested that use a custom queue is fine, but is not guaranteed to run on a background thread. – Andrea Jan 31 '15 at 18:08

3 Answers3

1

Ranjit: Based on your comments in the different responses, I suspect you are sending the 1st request from the main thread. When you receive the 1st response, you process it in the background, and then send the 2nd request also from the background. The subsequent requests should be sent from the main thread

[self performSelectorOnMainThread:@selector(myMethodToOpenConnection:)
                       withObject:myObject
                    waitUntilDone:NO];

otherwise the thread exits before the delegate is called

Eric D'Souza
  • 680
  • 5
  • 21
0

You can use NSOperation Queue with NSURLConnection like this

//allocate a new operation queue
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//Loads the data for a URL request and executes a handler block on an
//operation queue when the request completes or fails.
[NSURLConnection
 sendAsynchronousRequest:urlRequest
 queue:queue
 completionHandler:^(NSURLResponse *response,
                     NSData *data,
                     NSError *error) {
     if ([data length] >0 && error == nil){
         //process the JSON response
         //use the main queue so that we can interact with the screen
         NSString *myData = [[NSString alloc] initWithData:data
                                                  encoding:NSUTF8StringEncoding];
         NSLog(@"JSON data = %@", myData);
         NSDictionary *myDict = [myData JSONValue];
         }
 }];

it will do all the processing in the background.

Muhammad Waqas Bhati
  • 2,775
  • 20
  • 25
  • hey what is NSOperationQueue doing here? – Ranjit Jan 31 '15 at 10:45
  • sendAsynchronousRequest will send Asnyc request and nsoperation queue will send and handle response on background thread.Secondly you can modify your own code too Just send request on main thread and when connectionDidFinishLoading is called then handle response data on background thread using GCD. – Muhammad Waqas Bhati Jan 31 '15 at 11:03
  • your app is stucking because you are performing heavy data operation on main thread. – Muhammad Waqas Bhati Jan 31 '15 at 11:05
  • I thought of sending request on main thread and processing response on background thread, but the problem is that, as I iterate through the loop and send data one by one, suppose I have 3 notes which are added by user and which needs to be synced to server, I run through my loop, I send first note, I get response , I save it to coredata db and then start sending second request, so now if only processing of response is done in background thread, then it will not handle this properly. – Ranjit Jan 31 '15 at 14:45
  • Did you want this in a serial form ? or what happened with using this approach ? – Muhammad Waqas Bhati Jan 31 '15 at 14:50
  • when you get response of first note then send second note and again repeat this process for third .....it will save data serially – Muhammad Waqas Bhati Jan 31 '15 at 14:58
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/69957/discussion-between-ranjit-and-muhammad-waqas). – Ranjit Jan 31 '15 at 14:59
0

NSURLConnection provides a convenience method called sendAsynchronousRequest: completionHandler: that does the GCD work for you. You can tell it to run the completion handler on the main thread.

Using it, your code would get simpler as follows:

// place a declaration in your .h to make it public
- (void)pushDataWithCompletion:(void (^)(BOOL, NSError*))completion;

- (void)pushDataWithCompletion:(void (^)(BOOL, NSError*))completion
{
    // setup your connection request...
    [[request setValue:@"application/json;charset=utf-8" forHTTPHeaderField:@"Content-Type"];
    [request setHTTPMethod:@"POST"];
    [request setHTTPBody:jsonData];

    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {

        // whatever you do on the connectionDidFinishLoading
        // delegate can be moved here
        if (!error) {
            // did finish logic here, then tell the caller you are done with success
            completion(YES, nil);
        } else {
            // otherwise, you are done with an error
            completion(NO, error);
        }
    }];
}

Exactly what you pass back in the block depends on what the callers care about. It's common to make some aspect of the data you collected one of the block params.

EDIT - I left out the pointer notation (*) after NSError above.

Also, say you have an array of objects that needs to be processed by the server. This method is good for one call. To handle several, lets give it a parameter. Say that each note is an NSString *;

- (void)pushNote:(NSString *)note withCompletion:(void (^)(BOOL, NSError*))completion {
    // Code is the same except it forms the request body using the note parameter.
}

If the real task is to do work for several notes, you need a method that calls this one repeatedly, then tells its caller that its done.

- (void)pushNotes:(NSArray *)notes withCompletion:(void (^)(BOOL, NSError*))completion {

    // if there are no more notes, we are done
    if (!notes.count) return completion(YES, nil);

    NSString *nextNote = notes[0];
    NSArray *remainingNotes = [notes subarrayWithRange:NSMakeRange(1, notes.count-1)];

    [self pushNote:nextNote withCompletion:^(BOOL success, NSError*error) {
        // if success, do the rest, or else stop and tell the caller
        if (success) {
            [self pushNotes:remainingNotes withCompletion:completion];
        } else {
            completion(NO, error);
        }
    }]; 
}
danh
  • 62,181
  • 10
  • 95
  • 136
  • so @danh, whats wrong with my previous delegate, connectionFinishLoading?. Also I didnt get your last two lines. Can you please explain it – Ranjit Jan 31 '15 at 14:37
  • I don't see anything wrong with the code that you posted except it makes it inconvenient to tell a caller that you're done. The block style lets you pass information back to the caller. The last two lines just say that what you want to pass back is up to you. Whatever it is, make it a parameter to the completion block. – danh Jan 31 '15 at 14:42
  • so if I use this method, their is no need to use GCD? and my UI will not get stuck? @danh – Ranjit Jan 31 '15 at 14:47
  • That method uses GCD for you. The "mainQueue" parameter tells it to run your completion code on the main thread. So the UI should perform normally while the request is being serviced, then update normally (on the main thread) when its done. – danh Jan 31 '15 at 14:51
  • one more thing @danh, I forgot to mention, as I iterate through the loop and send data one by one, suppose I have 3 notes which are added by user and which needs to be synced to server, I run through my loop, I send first note, I get response , I save it to coredata db and then start sending second request. So whether this will be taken care? – Ranjit Jan 31 '15 at 14:54
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/69960/discussion-between-ranjit-and-danh). – Ranjit Jan 31 '15 at 15:33
  • Hello @danh, http://stackoverflow.com/questions/28298269/process-json-data-coming-from-server-and-add-update-the-objects-in-coredata – Ranjit Feb 03 '15 at 12:35