views:

142

answers:

5

I have an app that needs to send date (using POST) to a server. This function has to be on one of the NavigationController sub-controllers and user should be able to navigate away from this controller and/of close the app (only iPhone4/iOS4 will be supported). Should I use threads/NSOperations or/and send data using existing asynchronous methods? Any ideas/best practices how to implement this?

A: 

I'd definitely suggest a second thread for any long running process which need to run whilst the user is doing something else.

Another thing you will need to think about is what is going to happen if the user starts the process and then hits the home button. How will the server interaction be effected by being interrupted? Can it continue when the user next enters the app? etc.

Derek Clarkson
these are exactly issues I need to investigate. If sending data in the new thread, do I need to use async oder sync methods to post data?..
Konstantin
A: 

Use ASIHTTP and setup a Queue. All the information you need can be found here:

http://allseeing-i.com/ASIHTTPRequest/

This is the easiest way to accomplish what you want to accomplish. For sending lots of data, it is better to send in the background to keep the UI responsive. ASIHTTPRequest provides all the methods you need to kick of multiple queries (i.e. progress checks, callbacks, etc).

It's used by tons of great iPhone apps.

Jordan
And this was voted down why?
Jordan
Because good networking code doesn't require a separate thread. What do you mean "setup an async queue"? A NSOperationQueue? That's completely unnecessary.
tc.
I meant having the post run in the background to not tie up the UI.
Jordan
And how could I keep this Queue for executing in background (after closing the app)?
Konstantin
Background processing such as what you want to do is not allowed by Apple, for Apps that are not in the foreground. Please take a look at the ASIHTTPRequest answer I provided above. It's all you need.
Jordan
It's allowed on iOS4 for about 10 minutes, like tc said already
Konstantin
A: 

I'd just use NSURLConnection. It's a bit tricky if you want to send multipart/form-data (see the SimpleURLConnections/PostController.m example). I'd stick it in the app delegate, but I'm lazy like that.

You shouldn't worry about threads at all unless non-blocking I/O (i.e. NSURLConnection) is too slow. Threading has its own overheads, and inter-thread communication is a pain, and deadlocks are terrible.

What you do need to do is start a background task to allow your app to continue executing while backgrounded (end the background task in connectionDidFinishLoading: and connection:didFailWithError). Backgrounded apps are given about 10 minutes to finish executing background tasks.

tc.
ok, and that's the question: how do I allow the already started thread (here: NSURLConnection) to continue executing in the background after app closing?
Konstantin
A: 

OK, I'll answer my own question. First, like tc said, it's better to have this call on the application delegate, so that the View in the NavigationController can be closed. Second, mark beginning of the background processing with beginBackgroundTaskWithExpirationHandler: and end it with endBackgroundTask: like this:

.h:

UIBackgroundTaskIdentifier bgTask;

.m:

- (void)sendPhoto:(UIImage *)image
{
  UIApplication *app = [UIApplication sharedApplication];

  bgTask = [app beginBackgroundTaskWithExpirationHandler:^{ 
    [app endBackgroundTask:bgTask]; 
    bgTask = UIBackgroundTaskInvalid;
  }];


  NSLog(@"Sending picture...");

  // Init async NSURLConnection

  // ....
}

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

  NSLog(@"Picture sent.");

  UIApplication *app = [UIApplication sharedApplication];

  if (bgTask != UIBackgroundTaskInvalid) {
    [app endBackgroundTask:bgTask]; 
    bgTask = UIBackgroundTaskInvalid;
  }
}

You have 10 minutes before iOS terminates your app. You can check this time with [app backgroundTimeRemaining]

Konstantin
A: 

I'd like to support the post that mentions:

bgTask = [app beginBackgroundTaskWithExpirationHandler:^{

 [app endBackgroundTask:bgTask]; 

 bgTask = UIBackgroundTaskInvalid;

}];

But also point out that you may want to encapsulate your unit of work in an NSOperation subclass as well. This will make it extremely re-useable and, when combined with NSOperationQueue, automatically handle threading and what not. Then later, when you want to change your code, or have it appear in a different location in your app, it will be trivial to move or edit.

One note about using the operation queue, is that in this case you will actually want to send a synchronous url request from within the queue. This will let you not have to worry about concurrent operations. Here is the link that you may find helpful:

http://www.cimgf.com/2008/02/16/cocoa-tutorial-nsoperation-and-nsoperationqueue/

Jackie Treehorn