Skip to main content

How to Gather URLSession Connection Metrics

Tags

URLSession Connection Metrics

If you are like me you may be spending a good deal of development time debugging network connections trying to know more about where time is being spent in your connection life-cycle.  If you are using URLSession or Apple's Network Framework in your iOS or macOS application then you may want to take a look at the connection metrics APIs available.  One of those APIs is URLSessionTaskTransactionMetrics, and it is a metric gathering set of APIs built right into URLSession for gathering data like DNS time, TLS time, connection setup time, and much more.  I have found the URLSessionTaskTransactionMetrics APIs extremely useful and that is why I wanted to write this tutorial, to provide insight to other developers who may be looking for this same information and to show an example of all the data available.  Note, that there are connection metric APIs available in Network Framework as well, but that is not covered in this tutorial.

 

Setting up URLSessionTaskTransactionMetrics 🚀

To get started, you will first want to setup URLSession and URLSessionConfiguration objects that make sense for your application networking needs.  In the declaration of the URLSession object, you will want to instruct the object to use the parent class as the URLSessionDataDelegate.  This will allow your class to receive the delegate callback data for `didFinishCollecting metrics.`  Let's take a look a how this code is implemented below.

  1. Setup class level objects for urlConfiguration and urlSession.
  2. Instruct URLSessions that NetworkManager is a URLSessionDataDelegate.
  3. Receive metric data for your network request.
class NetworkManager: NSObject {
 
    static let shared = NetworkManager()
    // (1) Setup class level objects for urlConfiguration and urlSession.
    public let urlConfiguration = URLSessionConfiguration.default
    public var urlSession: URLSession?
 
    private override init() {
        super.init()
 
        // (2) Instruct URLSessions that NetworkManager is a URLSessionDataDelegate.
        self.urlSession = URLSession(configuration: urlConfiguration, delegate: self, delegateQueue: OperationQueue.main)
    }
}
 
extension NetworkManager : URLSessionDataDelegate {
    // (3) Receive metric data for your network request.
    func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) {
        for metric in metrics.transactionMetrics {
            print(metric)
        }
    }
}

Now that the URLSession object and the URLSessionDataDelegate structure is set, let's send a request to start capturing some connection metrics.  The code below is just a very simple GET request using URLSession to access NASA's open planetary APIs.  

extension NetworkManager {
 
    func getRequest() {
        guard let getURL = URL(string: "https://api.nasa.gov/planetary/apod?api_key=NNKOjkoul8n1CH18TWA9gwngW1s1SmjESPjNoUFo") else {
            return
        }
 
        var getRequest = URLRequest(url: getURL)
        getRequest.httpMethod = "GET"
 
        // Send the GET dataTask here
        let getRequestTask = self.urlSession?.dataTask(with: getRequest, completionHandler: { (data, response, error) in
 
            // Handle response
 
        })
        getRequestTask?.resume()
    }
}

When this code is executed it will produce URLSession connection metrics that look similar to the data below.

(Fetch Start) 2019-07-23 02:11:46 +0000
(Domain Lookup Start) 2019-07-23 02:11:47 +0000
(Domain Lookup End) 2019-07-23 02:11:47 +0000
(Connect Start) 2019-07-23 02:11:47 +0000
(Secure Connection Start) 2019-07-23 02:11:47 +0000
(Secure Connection End) 2019-07-23 02:11:48 +0000
(Connect End) 2019-07-23 02:11:48 +0000
(Request Start) 2019-07-23 02:11:48 +0000
(Request End) 2019-07-23 02:11:48 +0000
(Response Start) 2019-07-23 02:11:48 +0000
(Response End) 2019-07-23 02:11:48 +0000
(Protocol Name) http/1.1
(Proxy Connection) NO
(Reused Connection) NO
(Fetch Type) Network Load
(Request Header Bytes) 271
(Request Body Transfer Bytes) 0
(Request Body Bytes) 0
(Response Header Bytes) 436
(Response Body Transfer Bytes) 757
(Response Body Bytes) 1366
(Local Address) 192.168.1.10
(Local Port) 52343
(Remote Address) 34.205.11.10
(Remote Port) 443
(TLS Protocol Version) 0x0303
(TLS Cipher Suite) 0xC02F
(Cellular) NO
(Expensive) NO
(Constrained) NO
(Multipath) NO

 

In Summary ⌛️

In Summary, the URLSessionTaskTransactionMetrics APIs expose data about the lifecycle of a request that has proven very helpful for me when trying to piece together connection setup time, dns time, and also TLS setup and secure handshake time in packet traces.  This exposes a glimpse into where the latency is in your connections and how you can work with your server team to get to the bottom of it.  I hope you enjoyed this tutorial.  If you have any questions, comments or concerns, please feel free to leave a comment and I will get back to you as soon as I can.  Thank you!

Member for

3 years 9 months
Matt Eaton

Long time mobile team lead with a love for network engineering, security, IoT, oss, writing, wireless, and mobile.  Avid runner and determined health nut living in the greater Chicagoland area.