In 2018, 52.2% of all website traffic worldwide was generated through mobile phones, which is roughly double what it was in 2014. As these mobile traffic numbers continue to rise, so does the volume of new mobile apps being introduced, along with the velocity of their releases. In this hyper-competitive mobile landscape, users today expect more sophisticated apps and richer experiences, and they want it all delivered in the blink of an eye regardless of network conditions. So mobile app developers are under more pressure than ever to meet these (often contradictory) demands.
In this blog post, we’ll focus specifically on iOS developers and ways to help them meet these challenges by focusing on networking.
As the Apple ecosystem has become more sophisticated, many iOS developers have become used to receiving APIs with a very high level of abstraction that do most of the heavy lifting. For example, with these kinds of APIs you can paint the table view with a bunch of delegate and datasource methods without worrying about the UI rendering logic. As a result of this kind of heavy lifting being handled for you, it’s natural for developers to focus more on the application logic and less on other aspects of app development, performance, and UX.
But there are also valuable benefits to staying current with all network-related updates, and to understanding how to put the latest network capabilities to work for you.
So let’s look at five topics related to iOS network capabilities that can help you deliver rich user experiences with peak performance:
- How to choose between a foundation framework and a network framework
- Why to enable HTTP/2
- How to reduce latency
- How to improve throughput
- How to design for failures
How to choose between a foundation framework and a network framework
Cellular networks are unreliable, which can cause the networking code in mobile applications to be complex. When you build your applications, it’s important to evaluate the complexity of your applications, then choose the best layer of APIs to fit your needs. Let’s look at two specific layers of APIs: foundation.framework and network.framework.
Foundation is a family of responsive front-end frameworks that can help you deliver a richer user experience in your app. For example, let’s look at a simple networking task: an app that retrieves weather data and displays it on the screen. This is a very simple networking need that could be satisfied by using the Foundation framework-level API. URLSession is used to upload and download the data by specifying the URLs, so you can simply specify the type of task and get the job done. In this example, we could specify the GET request with the URL to retrieve the weather data:
At WWDC 2018, Apple introduced Network.framework, a modern alternative to BSD sockets. Prior to this introduction, frameworks like CFNetwork built over BSD sockets were often used to somewhat inefficiently solve lower-level networking needs in iOS. With Network.framework, Apple aims to more efficiently handle connection establishment, data transfer, and mobility.
Note: For a more detailed look at the Network.framework, see Apple’s overview video.
To discuss Network.framework more specifically, let’s get back to our earlier example of a weather app. Imagine that you are upgrading the app to a professional-grade experience that could be used by a meteorologist. The server logic must become highly advanced and must use a historical data model to predict meteorological activities. In this use case, there would be huge volumes of data that should be pushed to the app based on event triggers.
You could still use the URLSession and just poll the server more frequently to provide all the needed data. However, this is not an optimal solution because you don’t know whether you have the freshest set of data available; you may be just regurgitating stale data. Also, there would likely be a performance hit when you continually exploit networking resources.
For this more sophisticated app, you need to dive one level deeper and use Network.framework to directly access protocols like TLS, TCP, and UDP. With Network.framework libraries, you can establish bidirectional connections and ensure that the app gets all the live updates it requires without any inefficiencies.
Now that you’re current on choosing the best layer of APIs, let’s look at other performance-enhancing techniques.
Why to enable HTTP2
Apps talk to servers by using the HTTP protocol. HTTP/2, released in 2015, is a major revision that solves some of the inherent network issues caused by the nature of HTTP/1.1. Most importantly, mobile apps load media considerably faster on HTTP/2. Here are a few of the key differences between the two:
HTTP/2 vs. HTTP/1.1
HTTP/2 is based on binary data and has better compression, which reduces errors and inefficiencies.
HTTP/1.1 supports only one active connection, resulting in poor network utilization because of multiple connections.
HTTP/1.1 has a head-of-line blocking problem; this means that the server won’t handle a given request until the client has fully digested the response of the previous request. HTTP/2, on the other hand, supports logical request multiplexing over a single connection, which resolves this issue.
With HTTP/2, mobile devices can leverage server push to prefetch objects based on the current request, which improves performance.
By default, URLSession supports HTTP/2, so no changes to your source code are required. You only need to ensure that your server supports HTTP/2.
At WWDC 2018, Apple added HTTP/2 coalescing, a new behavior in the URLSession. Imagine if our hypothetical weather app requests an API response from the endpoint https://www.openweathermap.org and the server presents the certificate. Before the new coalescing behavior was introduced, you would have to tap on a button which made the request to another endpoint https://www.api.openweathermap.org. The previous certificates covered all the subdomains, but the URLSession was still making two different connections.
This process has changed with Apple's new update to the URLSession with HTTP/2 coalescing. URLSession now creates only one connection for those two endpoints. This removes the considerable overhead of creating the new connections, so the app loads much faster than before.
How to reduce latency
In simple terms, latency is any delay in the round-trip time it takes for a request from a mobile device to reach the server and come back after processing. Latency, bandwidth, and throughput are all inter-related. Here are three ways you can design your app to reduce latency:
Use fewer URLSession objects: URLSession objects are expensive, in data terms. It’s a common practice to abstract at the task level and use different sessions for various kinds of tasks; but by doing this, you waste a lot of networking resources by spinning off multiple connections, which results in suboptimal mobile user experiences.
Cache at device: URLSession supports caching by using the delegate method. It’s good practice to understand the load times and list the top 20 static resources as good candidates for caching. You should only cache static content. Here’s how to do this:
Use a CDN: With a content delivery network (CDN), you can cache static resources at the edge of the network, so users can retrieve the resources without any perceived latency.
How to improve throughput
Throughput is typically defined as the amount of data that can pass through a channel successfully. Here are three ways you can design your iOS app to improve throughput:
- Reduce the number of requests: You can optimize mobile throughput by keeping the number of requests in check. To that end, it’s a good practice to design your APIs to take multiple requests as the query parameters. Consider using technologies like GraphQL and Apollo iOS, which provide flexible, query-based APIs that can help you minimize the number of requests.
- Use HTTP compression: By compressing the HTTP data traveling between the mobile device and the server, the maximum bandwidth can then carry more data, which increases throughput. URLSession supports gzip and Brotli, which are both helpful compression algorithms. Plus, by moving to HTTP/2, you get header compressions by default, which further reduces the request bytes on the HTTP headers.
How to design for failures
Mobile devices often communicate over cellular networks that are subject to various types of failures and so, as developers, we should prepare our apps to encounter these potential network failures. Here are three measures you can employ:
Use the URLSession Adaptable Connectivity API: Frequently, there are instances when a user makes a request from the application while covered by a powerful WiFi network, but then walks out of the building and is suddenly connected to a weak cellular network. It’s very difficult to predict this network behavior, which, in turn, makes it difficult to provide a consistent user experience. With the Adaptable Connectivity feature of URLSession, you can outsource the task of writing the retrying logic to the library. All you need to do is to make a connection with the waitsForConnectivity option. Here’s how that looks:
For more on the URLSession Adaptable Connectivity API, see page 9 of this deck.
Leverage background sessions: Even under poor network conditions, you can still deliver an optimal experience by using background sessions and multitasking the requests to make better use of the system resources. With background sessions, it’s possible to upload or download large content when the user is under-utilizing the system resources.
Handle failures gracefully: Whenever there is a failed transaction, the user should not be left to wonder what went wrong with the mobile app. There are instances where the majority of requests fail, and showing one alert view for each failure would ruin the user experience. Some developers use the userInteractionEnabled flag in the UIKit to disable the interface when the request is sent out. We recommend using this flag cautiously; the entire app will be stateless and inactive if the flag is not re-enabled in the completion handler or delegate method at the time of the response.
Because mobile technology changes so rapidly, it’s critical to be sure your apps live up to the latest benchmarks for performance, and hopefully this blog post will help you get there. I also highly recommend using Network Link Conditioner, the Wireshark network protocol analyzer, and tcptrace to profile network performance. And of course, it’s just as important to stay current with the latest networking topics in the mobile space; HTTP/3, QUIC, Multipath TCP, and ECN are all topics that mobile developers should continue to monitor closely.
Gokul Sengottuvelu is a developer evangelist at Akamai Technologies.