Making HTTP and HTTPS Requests

OS X and iOS provide a number of general-purpose APIs for making HTTP and HTTPS requests. With these APIs, you can download files to disk, make simple HTTP and HTTPS requests, or precisely tune your request to the specific requirements of your server infrastructure.

When choosing an API, you should first consider why you are making an HTTP request:

The sections below describe these APIs in more detail.

Making Requests Using Foundation

The following tasks describe common operations with the NSURLSession class, the NSURLConnection class, and related classes.

Retrieving the Contents of a URL without Delegates

If you just need to retrieve the contents of a URL and do something with the results at the end, in OS X v10.9 and later or iOS 7 and later, you should use the NSURLSession class. You can also use the NSURLConnection class for compatibility with earlier versions of OS X and iOS.

To do this, call one of the following methods: dataTaskWithRequest:completionHandler: (NSURLSession), dataTaskWithURL:completionHandler: (NSURLSession), or sendAsynchronousRequest:queue:completionHandler: (NSURLConnection). Your app must provide the following information:

  • As appropriate, either an NSURL object or a filled-out NSURLRequest object that provides the URL, body data, and any other information that might be required for your particular request.

  • A completion handler block that runs whenever the transfer finishes or fails.

  • For NSURLConnection, an NSOperation queue on which your block should run.

If the transfer succeeds, the contents of the request are passed to the callback handler block as an NSData object and an NSURLResponse object for the request. If the URL loading system is unable to retrieve the contents of the URL, an NSError object is passed as the third parameter.

Retrieving the Contents of a URL with Delegates

If your app needs more control over your request, such as controlling whether redirects are followed, performing custom authentication, or obtaining the data piecewise as it is received, you can use the NSURLSession class with a custom delegate. For compatibility with earlier versions of OS X and iOS, you can also use the NSURLConnection class with a custom delegate.

For the most part, the NSURLSession and NSURLConnection classes work similarly at a high level. However, there are a few significant differences:

  • The NSURLSession API provides support for download tasks that behave much like the NSURLDownload class. This usage is described further in “Downloading Files to Disk.”

  • When you create an NSURLSession object, you provide a reusable configuration object that encapsulates many common configuration options. With NSURLConnection, you must set those options on each connection independently.

  • An NSURLConnection object handles a single request and any follow-on requests.

    An NSURLSession object manages multiple tasks, each of which represents a single URL request and any follow-on requests. You usually create a session when your app launches, then create tasks in much the same way that you would create NSURLConnection objects.

  • With NSURLConnection, each connection object has a separate delegate. With NSURLSession, the delegate is shared across all tasks within a session. If you need to use a different delegate, you must create a new session.

When you initialize an NSURLSession or NSURLConnection object, the connection or session is automatically scheduled in the current run loop in the default run loop mode.

The delegate you provide receives notifications throughout the connection process, including intermittent calls to the URLSession:dataTask:didReceiveData: or connection:didReceiveData: method when a connection receives additional data from the server. It is the delegate’s responsibility to keep track of the data it has already received, if necessary. As a rule:

  • If the data can be processed a piece at a time, do so. For example, you might use a streaming XML parser.

  • If the data is small, you might append it to an NSMutableData object.

  • If the data is large, you should write it to a file and process it upon completion of the transfer.

When the URLSession:task:didCompleteWithError: or connectionDidFinishLoading: method is called, the delegate has received the entirety of the URL’s contents.

Downloading the Contents of a URL to Disk

In OS X v10.9 and later or iOS 7 and later, if you need to download a URL and store the results as a file, but do not need to process the data in flight, the NSURLSession class lets you download the URL directly to a file on disk in a single step (as opposed to loading the URL into memory and then writing it out yourself). The NSURLSession class also allows you to pause and resume downloads, restart failed downloads, and continue downloading while the app is suspended, crashed, or otherwise not running.

In iOS, the NSURLSession class also launches your app in the background whenever a download finishes so that you can perform any app-specific processing on the file.

To use the NSURLSession class for downloading, your code must do the following:

  1. Create a session with a custom delegate and the configuration object of your choice:

    • If you want downloads to continue while your app is not running, you must provide a background session configuration object (with a unique identifier) when you create the session.

    • If you do not care about background downloading, you can create the session using any of the provided session configuration object types.

  2. Create and resume one or more download tasks within the session.

  3. Wait until your delegate receives calls from the task or session. In particular, you must implement the URLSession:downloadTask:didFinishDownloadingToURL: method to do something with a file when the download finishes and the URLSession:task:didCompleteWithError: call to handle any errors.

Making a POST Request

You can make an HTTP or HTTPS POST request in nearly the same way you would make any other URL request (described in “Retrieving the Contents of a URL with Delegates”). The main difference is that you must first configure the NSMutableURLRequest object you provide to the initWithRequest:delegate: method.

You also need to construct the body data. You can do this in one of three ways:

  • For uploading short, in-memory data, you should URL-encode an existing piece of data. This process is described in “Encoding URL Data” in URL Loading System Programming Guide.

  • For uploading file data from disk, call the setHTTPBodyStream: method to tell NSMutableURLRequest to read from an NSInputStream and use the resulting data as the body content.

  • For large blocks of constructed data, call CFStreamCreateBoundPair to create a pair of streams, then call the setHTTPBodyStream: method to tell NSMutableURLRequest to use one of those streams as the source for its body content. By writing into the other stream, you can send the data a piece at a time.

    Depending on how you handle things on the server side, you may also want to URL-encode the data you send.)

To specify a different content type for the request, use the setValue:forHTTPHeaderField: method. If you do, make sure your body data is properly formatted for that content type.

To obtain a progress estimate for a POST request, implement a connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite: method in the connection’s delegate.

Configuring Authentication

Performing authentication with NSURLSession and NSURLConnection is relatively straightforward. The way you do this depends on the class you use and on the version of OS X or iOS that you are targeting.

For the NSURLSession class, your delegate should implement the URLSession:task:didReceiveChallenge:completionHandler: method. In this method, you perform whatever operations are needed to determine how to respond to the challenge, then call the provided completion handler with a constant that indicates how the URL Loading System should proceed and, optionally, a credential to use for authentication purposes.

For the NSURLConnection class:

Possible Responses to an Authentication Challenge

Regardless of which class you use, your authentication handler method must examine the authentication challenge and tell the URL Loading System how to proceed:

Creating a Credential Object

Within your delegate’s connection:willSendRequestForAuthenticationChallenge: or connection:didReceiveAuthenticationChallenge: method, you may need to provide an NSURLCredential object that provides the actual authentication information.

Further Information

To learn more about the NSURLSession API, read URL Loading System Programming Guide. For related sample code, see SimpleURLConnections, AdvancedURLConnections, and Using NSXMLParser to parse XML documents.

For details about the NSURLConnection API, read URL Loading System Programming Guide.

To learn more about using the NSStream API for making HTTP requests, read “Setting Up Socket Streams” in Stream Programming Guide.

For an example of the setHTTPBodyStream: method and the CFStreamCreateBoundPair function, see SimpleURLConnections in the iOS library. (The sample as a whole is designed to build and run on iOS, but the networking portions of the code are also useful on OS X.)

Making Requests Using Core Foundation

Other than the syntax details, the request functionality in Core Foundation is closely related to what is available at the Foundation layer. Thus, the examples in “Making Requests Using Foundation” should be helpful in understanding how to make requests using the CFHTTPStream API.

The Core Foundation URL Access Utilities are a C-language API that is part of the Core Foundation framework. To learn more, read Core Foundation URL Access Utilities Reference.

The CFHTTPStream API is a C-language API that is part of the Core Foundation framework. (You can, of course, use it in Objective-C code.) To learn more, read “Communicating with HTTP Servers” and “Communicating with Authenticating HTTP Servers” in CFNetwork Programming Guide.

These APIs are the most flexible way to communicate with an HTTP server (short of using sockets or socket streams directly), providing complete control over the message body as sent to the remote server, and control over most of the message headers as well. These APIs are also more complex, and thus should be used only if higher-level APIs cannot support your needs—for example, if you need to override the default system proxies.

Working with Web Services

If you are incorporating client-side web services communication in your OS X program, you can take advantage of a number of technologies:

In addition, a number of third-party libraries exist for working with web services.