NSURLConnection
provides the most flexible method of downloading the contents of a URL. It provides a simple interface for creating and canceling a connection, and supports a collection of delegate methods that provide feedback and control of many aspects of the connection. These classes fall into five categories: URL loading, cache management, authentication and credentials, cookie storage, and protocol support.
In order to download the contents of a URL, an application needs to provide a delegate object that, at a minimum, implements the following delegate methods: connection:didReceiveResponse:
, connection:didReceiveData:
, connection:didFailWithError:
and connectionDidFinishLoading:
.
The example in Listing 1 initiates a connection for a URL. It begins by creating an NSURLRequest
instance for the URL, specifying the cache access policy and the timeout interval for the connection. It then creates an NSURLConnection instance, specifying the request and a delegate. If NSURLConnection can’t create a connection for the request, initWithRequest:delegate:
returns nil
. If the connection is successful, an instance of NSMutableData
is created to store the data that is provided to the delegate incrementally.
Listing 1 Creating a connection using NSURLConnection
// Create the request. |
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.apple.com/"] |
cachePolicy:NSURLRequestUseProtocolCachePolicy |
timeoutInterval:60.0]; |
// create the connection with the request |
// and start loading the data |
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self]; |
if (theConnection) { |
// Create the NSMutableData to hold the received data. |
// receivedData is an instance variable declared elsewhere. |
receivedData = [[NSMutableData data] retain]; |
} else { |
// Inform the user that the connection failed. |
} |
The download starts immediately upon receiving the initWithRequest:delegate:
message. It can be canceled any time before the delegate receives a connectionDidFinishLoading:
or connection:didFailWithError:
message by sending the connection a cancel
message.
When the server has provided sufficient data to create an NSURLResponse object, the delegate receives a connection:didReceiveResponse:
message. The delegate method can examine the provided NSURLResponse and determine the expected content length of the data, MIME type, suggested filename and other metadata provided by the server.
You should be prepared for your delegate to receive the connection:didReceiveResponse:
message multiple times for a single connection. This message can be sent due to server redirects, or in rare cases multi-part MIME documents. Each time the delegate receives the connection:didReceiveResponse:
message, it should reset any progress indication and discard all previously received data. The example implementation in Listing 2 simply resets the length of the received data to 0 each time it is called.
Listing 2 Example connection:didReceiveResponse: implementation
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response |
{ |
// This method is called when the server has determined that it |
// has enough information to create the NSURLResponse. |
// It can be called multiple times, for example in the case of a |
// redirect, so each time we reset the data. |
// receivedData is an instance variable declared elsewhere. |
[receivedData setLength:0]; |
} |
The delegate is periodically sent connection:didReceiveData:
messages as the data is received. The delegate implementation is responsible for storing the newly received data. In the example implementation in Listing 3, the new data is appended to the NSMutableData object created in Listing 1.
Listing 3 Example connection:didReceiveData: implementation
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data |
{ |
// Append the new data to receivedData. |
// receivedData is an instance variable declared elsewhere. |
[receivedData appendData:data]; |
} |
You can also use the connection:didReceiveData:
method to provide an indication of the connection’s progress to the user.
If an error is encountered during the download, the delegate receives a connection:didFailWithError:
message. The NSError object passed as the parameter specifies the details of the error. It also provides the URL of the request that failed in the user info dictionary using the key NSErrorFailingURLStringKey
.
After the delegate receives a message connection:didFailWithError:
, it receives no further delegate messages for the specified connection.
The example in Listing 4 releases the connection, as well as any received data, and logs the error.
Listing 4 Example connectionDidFailWithError: implementation
- (void)connection:(NSURLConnection *)connection |
didFailWithError:(NSError *)error |
{ |
// release the connection, and the data object |
[connection release]; |
// receivedData is declared as a method instance elsewhere |
[receivedData release]; |
// inform the user |
NSLog(@"Connection failed! Error - %@ %@", |
[error localizedDescription], |
[[error userInfo] objectForKey:NSErrorFailingURLStringKey]); |
} |
Finally, if the connection succeeds in downloading the request, the delegate receives the connectionDidFinishLoading:
message. The delegate will receive no further messages for the connection and the NSURLConnection object can be released.
The example implementation in Listing 5 logs the length of the received data and releases both the connection object and the received data.
Listing 5 Example connectionDidFinishLoading: implementation
- (void)connectionDidFinishLoading:(NSURLConnection *)connection |
{ |
// do something with the data |
// receivedData is declared as a method instance elsewhere |
NSLog(@"Succeeded! Received %d bytes of data",[receivedData length]); |
// release the connection, and the data object |
[connection release]; |
[receivedData release]; |
} |
This represents the simplest implementation of a client using NSURLConnection. Additional delegate methods provide the ability to customize the handling of server redirects, authorization requests and caching of the response.
By default the data for a connection is cached according to the support provided by the NSURLProtocol subclass that handles the request. An NSURLConnection Delegationdelegate can further refine that behavior by implementing connection:willCacheResponse:
.
This delegate method can examine the provided NSCachedURLResponse object and change how the response is cached, for example restricting its storage to memory only or preventing it from being cached altogether. It is also possible to insert objects in an NSCachedURLResponse’s user info dictionary, causing them to be stored in the cache as part of the response.
Note: The delegate receives connection:willCacheResponse:
messages only for protocols that support caching.
The example in Listing 6 prevents the caching of https
responses. It also adds the current date to the user info dictionary for responses that are cached.
Listing 6 Example connection:withCacheResponse: implementation
-(NSCachedURLResponse *)connection:(NSURLConnection *)connection |
willCacheResponse:(NSCachedURLResponse *)cachedResponse |
{ |
NSCachedURLResponse *newCachedResponse = cachedResponse; |
if ([[[[cachedResponse response] URL] scheme] isEqual:@"https"]) { |
newCachedResponse = nil; |
} else { |
NSDictionary *newUserInfo; |
newUserInfo = [NSDictionary dictionaryWithObject:[NSCalendarDate date] |
forKey:@"Cached Date"]; |
newCachedResponse = [[[NSCachedURLResponse alloc] |
initWithResponse:[cachedResponse response] |
data:[cachedResponse data] |
userInfo:newUserInfo |
storagePolicy:[cachedResponse storagePolicy]] |
autorelease]; |
} |
return newCachedResponse; |
} |
You can estimate the progress of an HTTP POST upload with the connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:
delegate method. Note that this is not an exact measurement of upload progress, because the connection may fail or the connection may encounter an authentication challenge.
NSURLConnection provides support for downloading the contents of an NSURLRequest in a synchronous manner using the class method sendSynchronousRequest:returningResponse:error:
. Using this method is not recommended, because it has severe limitations:
The client application blocks until the data has been completely received, an error is encountered, or the request times out.
Minimal support is provided for requests that require authentication.
There is no means of modifying the default behavior of response caching or accepting server redirects.
If the download succeeds, the contents of the request are returned as an NSData
object and an NSURLResponse
for the request is returned by reference. If NSURLConnection
is unable to download the URL, the method returns nil
and any available NSError
instance by reference in the appropriate parameter.
If the request requires authentication in order to make the connection, valid credentials must already be available in the NSURLCredentialStorage
, or must be provided as part of the requested URL. If the credentials are not available or fail to authenticate, the URL loading system responds by sending the NSURLProtocol
subclass handling the connection a continueWithoutCredentialForAuthenticationChallenge:
message.
When a synchronous connection attempt encounters a server redirect, the redirect is always honored. Likewise the response data is stored in the cache according to the default support provided by the protocol implementation.
Last updated: 2010-03-24