Capturing Network Requests

How to capture network requests

There are multiple options for logging network requests using Pulse.

Manual Logging #

The recommended option it to log the request manually using a NetworkLogger instance.

URLSession #

Add the following calls to your URLSessionDelegate.

let logger = NetworkLogger()

func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
    logger.logDataTask(dataTask, didReceive: response)
    // ... make sure to call a completionHandler
}

func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
    logger.logTask(task, didCompleteWithError: error)
    // ...
}

func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
    logger.logDataTask(dataTask, didReceive: data)
    // ...
}

func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) {
    logger.logTask(task, didFinishCollecting: metrics)
    // ...
}

The initial version only supports URLSessionDataTask.

Alamofire and Moya #

If you are using Alamofire or Moya, you can use any of the approaches outlined in this guide. For manual logging, you can use Alamofire.EventMonitor. It also works for Moya which uses on Alamofire.

import Alamofire

// Don't forget to bootstrap the logging system first.

let session = Alamofire.Session(eventMonitors: [NetworkLoggerEventMonitor(logger: logger)])

struct NetworkLoggerEventMonitor: EventMonitor {
    let logger: NetworkLogger

    func request(_ request: Request, didCreateTask task: URLSessionTask) {
        logger.logTaskCreated(task)
    }

    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
        logger.logDataTask(dataTask, didReceive: data)
    }

    func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) {
        logger.logTask(task, didFinishCollecting: metrics)
    }

    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
        logger.logTask(task, didCompleteWithError: error)
    }
}

Other #

NetworkLogger allows you to track the status of the request incrementally. But if you just want to store the response or you are using an API that doesn’t provide access to URLSessionTask instances, you can just store the response directly:

LoggerStore.default.storeRequest(request, response: response, error: error, data: data, metrics: nil)

Automatic Logging #

Manual logging is a simple and powerful option, but if you want to automate things a little bit or get access to URLSession instances you don’t have control over, there are options for you.

You can use URLSessionProxyDelegate to automatically capture all of the important URLSession events, including task metrics. You can either enable it for all session:

// Call it anywhere in your code prior to instantiating a `URLSession`
URLSessionProxyDelegate.enableAutomaticRegistration()

// Instantiate `URLSession` as usual
let session = URLSession(configuration: .default, delegate: YourURLSessionDelegate(), delegateQueue: nil)

URLSessionProxyDelegate uses Objective-C runtime to proxy the URLSessionDelegate calls and swizzling for automatic registration for all session.

If you want to enable logging just for your session, use URLSessionProxyDelegate directly.

let originalDelegate = YourURLSessionDelegate()
let proxyDelegate = URLSessionProxyDelegate(delegate: originalDelegate) 
let session = URLSession(configuration: .default, delegate: proxyDelegate, delegateQueue: nil)

If you are using a default URLSession with completion closure, the best option is to just store the responses manually in the completion closure using LoggerStore. But you can also give Experimental.URLSessionProxy a try. It’s not battle-tested yet, and it will affect your networking, so please use it at your own risk.

Experimental.URLSessionProxy.shared.isEnabled = true

The way it works is by registering a custom URLProtocol and using a secondary URLSession instance in it. This will affect your networking and should only be used for evaluating Pulse.