Table of Contents
One of the most straight-forward ways to improve user experience is to make things faster. For iOS apps that communicate with a web API one of the best ways to do this is by caching network requests. Employing effective caching strategies can make your app fast and responsive by reducing the number of requests made to the server and persisting data between launches.
Properly setting HTTP response headers in your web-API and taking advantage of iOS’s system-level network caching is a simple to implement strategy that can materially improve the performance of your mobile app.
Contained within every HTTP request/response is a set of metadata transferred as HTTP headers. This metadata describes request/response mime-type, status codes and several cache directives.
When a client makes a request to a server, the HTTP response includes a set of HTTP headers, including some which serve as directives for how that response should be cached:
Cache-Control- This header must be present in the response from the server to enable HTTP caching by a client. The value of this header may include information like its
max-age(how long to cache a response), and whether the response may be cached with
no-cache(not at all). See the
Cache-Controlsection of RFC 2616 for full details.
In addition to
Cache-Control, a server may send additional headers that can be used to conditionally request information as needed:
Last-Modified- The value of this header corresponds to the date and time when the requested resource was last changed. For example, if a client requests a timeline of recent photos,
Last-Modifiedvalue could be set to when the most recent photo was taken.
Etag- An abbreviation for “entity tag”, this is an identifier that represents the contents requested resource. In practice, an
Etagheader value could be something like the
MD5digest of the resource properties. This is particularly useful for dynamically-generated resources that may not have an obvious
Once a client receives a response with any of these cache headers, their values can be sent on subsequent requests to conditionally ask the server for information only if the resource has changed:
If-Modified-Since- This request header corresponds to the
Last-Modifiedresponse header. Set the value of this to the
Last-Modifiedvalue received from the last request to the same endpoint. If the resource has not been updated since the last request, the server will send a
304(not modified) status code response.
If-None-Match- This request header corresponds to the
Etagresponse header. Use the
Etagvalue received previously for the last request to that endpoint. If the value on the server is the same as requested by the client, then the resource has not been changed, and a
304(not modified) status code will be returned.
Server-side cache headers
Web frameworks of all languages support easily declaring the
Cache-Control and conditional headers. By way of example, the Sinatra framework in Ruby provides several helper methods that cleanly map to their header equivalents:
class App < Sinatra::Base get '/founders' do # ... cache_control :public, :must_revalidate, :max_age => 86400 last_modified Date.today etag Digest::MD5.hexdigest(@founders.to_s) # ... end end
There are a variety of browser-based tools that let you see the headers returned by your API including Firebug and Google Chrome’s built-in developer tools. Here you can see both the request and response cache-related headers when making a request to the sample application at http://floating-wind-3377.herokuapp.com/founders.
iOS network caching
The URL Loading System built-into Foundation classes like
NSURLCache, as well as frameworks that leverage them, such as AFNetworking will correctly follow HTTP directives sent from the server.
Heroku recommends CocoaPods as the preferred way to manage Xcode project dependencies, such as AFNetworking. See the article Getting Started with iOS Mobile Development and a Sinatra API for additional information.
NSURLCache automatically manages the caching behavior of incoming and outgoing network requests that use
NSURLConnection. If a cached response is available,
NSURLCache will transparently return the cached response without making a network request.
With a few simple adjustments, your iOS app has taken advantage of the powerful caching infrastructure provided by Foundation classes and HTTP.
- NSScreencast episode “HTTP Caching”
- Apple - URL Loading System Programming Guide
- RFC 2616 Section 14 - HTTP Header Field Definitions
- Sinatra docs for