Open source

Introducing Twitter Network Layer iOS framework for open source

By
Wednesday, 18 July 2018
Why a new framework?

In 2014, iOS 7 was released with the new NSURLSession API for networking to replace NSURLConnection (which ended up being deprecated in iOS 9). At Twitter, we wanted to modernize our original network code and design something scalable and robust that would last at least the next 10 years. We took that opportunity to assess the drawbacks of the system we had in place and enumerated all the possible improvements we could build in an encapsulated framework that was modern and scalable.

Twitter Network Layer (TNL) was first built in 2014 and over the course of  the next two years, we transitioned everything over to TNL while iterating on the framework. TNL has now been in production for 4 years, 100% adopted for over 2 years, and has had no bugs identified in the last 18 months. We now have measurements to assess our performance globally, have features to deliver reliability at scale which improved our network success rates dramatically, and have the extensibility to improve performance with features such as pluggable codecs to add Brotli support to all iOS devices, even before Brotli support was introduced with iOS 11.

Today, we're happy to share Twitter Network Layer with the world so everyone can enjoy the same benefits and build quality apps without having to fuss over their network layer.

What does TNL offer?

TNL is a framework that offers an enhanced interface to the NSURLSession network layer to add flexibility and scalability, and to encapsulate some of the complexities of advanced, robust networking.

The design of TNL is built for scaling network requests across a very large code base that runs on many millions of devices around the world. NSURLSession fundamentally has a scaling problem in that every unique combination of delegate (and delegate behavior) and configuration requires a new session object. With a small code base with few network requests, this works well, but with dozens of configurations and dozens of delegates for different behaviors run across thousands of different requests, the number of NSURLSession instances becomes unwieldy.

 

This post is unavailable
This post is unavailable.

This post is unavailable
This post is unavailable.

TNL addresses this complexity, allowing large teams to scale their networking across a large set of use cases, by decoupling the configuration and delegate from the session object. This makes requests, configurations, and delegates independently composable with one another without needing to balance dozens or hundreds of sessions.

There is an additional benefit of having TNL abstract away NSURLSession instances, which is an automatic performance gain of reducing the number of NSURLSessions that would need to be created.  By having this abstraction layer, TNL does not have to provide a different delegate per NSURLSession by muxing and demuxing the delegates you provide to TNL, which can dramatically reduce the number of NSURLSessions that need to be created.  Beyond this, TNL works to provide its own implementation of configuration settings where possible so that having variance in configuration do not have to yield multiple NSURLSessions. For example: by controlling the timeouts in TNL, we don't have to create a new NSURLSession for each change in timeout (such as long timeouts for large resources and shorter timeouts for quick ping requests). Apple recommends minimizing NSURLSession usage, which can be hard to manage when high degree of configurability and varied delegation is needed.  Thankfully, TNL automatically handles this for you.

Another design choice at the core of TNL is having every network request's "task" be a subclass of NSOperation. This means every request operation is composable with a dependency chain, cancellable while in progress, and has a priority that can be modified for the entire lifetime of the operation.

This post is unavailable
This post is unavailable.

From this foundation, TNL includes a number of features for easy use by any developer, including:

  • Ability to specify the execution mode
    - Execute as an in app request
    - Execute as an in app request that is automatically wrapped in a UIKit background task
    - Execute as a background request
  • Configurable consumption modes: Discard the data, save to memory (as NSData), save to file on disk, or deliver the data in chunks to a callback
  • Automatic network activity events, used for easy integration with the UIApplication's network activity indicator
  • Pluggable request content encoders and response content decoders
  • Better timeout patterns, though still limited by the amount of control NSURLSession layer offers
  • A plethora of events, both per request and global
  • Delegated dynamic decisions:
    - Authorizing requests
    - Request hydration (very powerful for simple concrete requests as a pattern)
    - Redirect
    - Retry policies, which can lead to greatly improved success rates
  • Concrete response objects:
    - Offers the state of the completion of the response
    - Provides detailed metrics for how the request performed
    - By subclassing response objects, you can provide custom state to your response such as if the response qualifies as success or parsing the response bytes into a concrete model object
  • Automatic NSOperation dependency adding when enqueuing a specified high priority request for all subsequent lower priority requests to depend upon.
  • Automatic backoff mode when HTTP 503 status codes are seen, giving your servers a chance to breathe from your app overwhelming them.
  • Clogged callback detection: helps catch an easy to write easy bug where delegate callbacks take too long or don't complete, but is often very hard to detect in order to fix.
  • A "pseudo" NSURLProtocol that allows canned responses with customizable network performance behaviors, which is useful for building tests

Last but not least, TNL encapsulates workarounds and solutions for bugs in native frameworks that are difficult to manage. As bugs crop up and NSURLSession changes, TNL is updated to support the Twitter for iOS app, and everyone using TNL can share in that benefit. Beyond bugs, iOS networking has some rough edges that are easy to make mistakes with that TNL will smooth over.

TNL offers a lot of great features and support, most of which are optional for when you want to take control of your networking to reach hundreds of millions of users. TNL's smart defaults make for easy adoption, and the detailed configurability offer amazing control for getting the specifics right.

Who is TNL for?

TNL has proven itself useful for anyone who wants the flexibility and power to build robust apps with wide ranges of network use cases at scale. We don't expect everyone to drop their network layer for TNL, but for those serious about scaling their networking with visibility and control, TNL can offer that and for larger apps that require a scalable network layer that works resiliently around the world. TNL offers these benefits with the added dependability of Twitter as a company keeping it a priority for the long term. For app developers wanting to just move fast, you should use what works best for your needs, whether that's with AFNetworking, NSURLSession or TNL. The benefit of using TNL when your app is small is that when your app goes global, you have the underlying framework to adapt with ease.

What about Android?

Great question! Twitter uses Square's OkHTTP (v3) for Twitter for Android. OkHTTP has been actively maintained and developed with strong community support, and we've been pleased with using it as our network layer on Android.

Where can I find TNL?

As with all Twitter open source projects, the Twitter Network Layer framework for iOS project can be found on Twitter’s github page, specifically at https://github.com/twitter/ios-twitter-network-layer. We hope you like it!

Be sure to check out our other open source projects, including the Twitter Logging Service framework for iOS (TLS) and Twitter Image Pipeline framework for iOS (TIP).

This post is unavailable
This post is unavailable.