14

I have an application that uses an API to get real time updates on the website. They use what they call a long-polling technique:

Long polling is a variation of the traditional polling technique and allows emulation of an information push from a server to a client. With long polling, the client requests information from the server in a similar way to a normal poll. However, if the server does not have any information available for the client, instead of sending an empty response, the server holds the request and waits for some information to be available. Once the information becomes available (or after a suitable timeout), a complete response is sent to the client. The client will normally then immediately re-request information from the server, so that the server will almost always have an available waiting request that it can use to deliver data in response to an event. In a web/AJAX context, long polling is also known as Comet programming.

Long polling is itself not a push technology, but can be used under circumstances where a real push is not possible.

Basically this enforces to make a request back to the server once you've got a response back. What is the best way to do this in an iphone application? This eventually has to run in the background.

PengOne
  • 48,188
  • 17
  • 130
  • 149
adit
  • 32,574
  • 72
  • 229
  • 373
  • It's not clear what are you looking for. It seems you have the concept in mind, so what is your question? – Manlio Jun 10 '11 at 00:32
  • Note that this won't work in the background on iOS unless you're doing VoIP, audio streaming, or GPS. Your app gets suspended otherwise. – Daniel Dickison Jun 10 '11 at 01:12

2 Answers2

20

This is exactly the sort of use-case that NSURLConnection sendSynchronousRequest is perfect for:

- (void) longPoll {
    //create an autorelease pool for the thread
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];

    //compose the request
    NSError* error = nil;
    NSURLResponse* response = nil;
    NSURL* requestUrl = [NSURL URLWithString:@"http://www.mysite.com/pollUrl"];
    NSURLRequest* request = [NSURLRequest requestWithURL:requestUrl];

    //send the request (will block until a response comes back)
    NSData* responseData = [NSURLConnection sendSynchronousRequest:request
                            returningResponse:&response error:&error];

    //pass the response on to the handler (can also check for errors here, if you want)
    [self performSelectorOnMainThread:@selector(dataReceived:) 
          withObject:responseData waitUntilDone:YES];

    //clear the pool 
    [pool drain];

    //send the next poll request
    [self performSelectorInBackground:@selector(longPoll) withObject: nil];
}

- (void) startPoll {
    //not covered in this example:  stopping the poll or ensuring that only 1 poll is active at any given time
    [self performSelectorInBackground:@selector(longPoll) withObject: nil];
}

- (void) dataReceived: (NSData*) theData {
    //process the response here
}

Alternately, you could use async I/O and delegate callbacks to accomplish the same thing, but that would really be kind of silly in this case.

aroth
  • 54,026
  • 20
  • 135
  • 176
  • 1
    What's to stop the `NSURLConnection` from failing with `NSURLErrorTimedOut` before the host sends back a response? How would you configure how long the long poll is when using `NSURLConnection`? – Jeremy W. Sherman Jun 10 '11 at 01:20
  • 4
    @Jeremy: [This answer](http://stackoverflow.com/questions/1424608/nsurlconnection-timeout) would cover that. Basically you just replace `requestWithUrl:` with `requestWithURL:cachePolicy:timeoutInterval:`, specifying an arbitrarily large timeout interval. – aroth Jun 10 '11 at 01:26
  • What is the benefit of going this route over using an async NSURLConnection? – Ross Hambrick Jan 26 '12 at 17:04
  • 1
    @hambonious - A simpler implementation (you don't have to implement any delegate protocols, for example) and a more intuitive control flow are a couple of benefits. Async I/O is really a bit overhyped, in my opinion. It is not always "better" than using synchronous I/O. – aroth Jan 26 '12 at 22:20
  • @aroth I found this code sample on pastebin and asked stackoverflow why this long-polling technique doesn't cause a stack overflow. It is recursive, right? – David Jul 08 '13 at 22:08
  • 1
    @David - No, it's not actually recursive because an invocation of `longPoll` does not wait for the next call to return before returning itself. It schedules the next call and then returns. It's basically the Objective-C equivalent of a [setTimeout() loop](http://stackoverflow.com/questions/8443151/how-to-stop-a-settimeout-loop) in JavaScript. Or equivalently, a [setInterval() call](http://www.w3schools.com/jsref/met_win_setinterval.asp). – aroth Jul 09 '13 at 00:32
6

Long polling is making a read request to a server, the server gets the requests, finds that there's nothing of interest to send you, and rather than returning nothing, or "empty", it instead holds on to the request until something interesting shows up. Once it finds something, it writes to the socket and the client receives the data.

The detail is that for this entire time, using generic socket programming, the client is blocked and hanging on the sockets read call.

There are two ways to deal with this (well, three if you don't mind being stuck on the main thread for several seconds, but let's not count that one).

  1. Put the socket handling code in a thread. In this case, the entire socket process is in an independent thread within the program, so it happily sits stuck on the read waiting for a response.

  2. Use asynchronous socket handling. In this case, your socket read does NOT block the main thread. Instead, you pass in call back functions that respond to the activity on the socket, and then go about your merry way. On the Mac, there's CFSocket which exposes this kind of functionality. It spawns its own thread, and manages the socket connection using select(2).

This is a nice little post talking about CFSocket.

CFSocket fits well in to the Mac idiom of message passing and eventing, and is probably what you should be looking at for doing this kind of work. There is also an Obj-C class wrapper built on CFSocket called ULNetSocket (formerly NetSocket).

Will Hartung
  • 115,893
  • 19
  • 128
  • 203