How to create a custom URLSession in Swift

Jan 06, 2024#swift#network

URLSession is a part of the Foundation framework in Swift, and it provides an API for making network requests. It allows you to perform tasks such as fetching data from a URL, uploading data, downloading files, and more.

There is a distinction between the shared URLSession and the default URLSessionConfiguration:

  • The shared URLSession is a singleton instance provided by the system. You access it through URLSession.shared. It uses a default session configuration with system-wide settings.
  • The default URLSessionConfiguration is used when you create a session without specifying a custom configuration. It has default settings suitable for common scenarios. You can create custom URLSessionConfiguration to configure the timeout values, caching policies, connection requirements, and other types of information that you intend to use with your URLSession object.

It is important to configure your URLSessionConfiguration object appropriately before using it to initialize a session object. Depending on your specific use case, you may need to adjust following properties:

let configuration = URLSessionConfiguration.default

// Caching:
configuration.requestCachePolicy = .returnCacheDataElseLoad  // Use cached data if available, otherwise load from server
configuration.urlCache = URLCache(memoryCapacity: 10 * 1024 * 1024, diskCapacity: 50 * 1024 * 1024, diskPath: nil)  // Set cache size

// Timeouts:
configuration.timeoutIntervalForRequest = 30  // Set request timeout to 30 seconds
configuration.timeoutIntervalForResource = 60  // Set resource loading timeout to 60 seconds

// Headers:
configuration.httpAdditionalHeaders = ["Authorization": "Bearer <token>"]  // Add authorization header

// Background downloads:
configuration.isDiscretionary = true  // Allow system to pause background downloads for efficiency

// Create session:
let session = URLSession(configuration: configuration)

There are three main types of sessions supported by URLSessionConfiguration: Default sessions are for common tasks with shared data, ephemeral sessions are for short-lived tasks without data persistence, and background sessions are for tasks that continue in the background with separate storage and lifecycle handling.

Default sessions

The default session is created using URLSessionConfiguration.default. It is suitable for most common networking tasks and uses shared storage for cache, cookies, and credentials.

// Create a configuration object
let config = URLSessionConfiguration.default
// Set the timeout interval
config.timeoutIntervalForRequest = 10
// Set a custom header
config.httpAdditionalHeaders = ["X-Custom-Header": "Hello"]
// Create a URLSession with the configuration
let session = URLSession(configuration: config)

let url = URL(string: "https://example.com/data")!

// Create a data task
let task = session.dataTask(with: url) { data, response, error in
    // ...
}

// Start the task
task.resume()

Ephemeral sessions

The ephemeral configuration URLSessionConfiguration.ephemeral is similar to the default configuration, except that it stores all of the session-related data in memory. This means that the caches, cookies, and credentials are not written to disk and are discarded when the session object is invalidated.

It’s useful for privacy-sensitive scenarios where you want to perform a series of short-lived network requests and don’t want to persist any data between app launches.

// Create an ephemeral session configuration
let ephemeralConfiguration = URLSessionConfiguration.ephemeral

// Create a URLSession with the ephemeral configuration
let ephemeralSession = URLSession(configuration: ephemeralConfiguration)

// Create a data task using the ephemeral session
let url = URL(string: "https://www.example.com/api/data")!
let dataTask = ephemeralSession.dataTask(with: url) { (data, response, error) in
    // Handle the response or error
}

// Resume the data task
dataTask.resume()

Background sessions

Background sessions provide a way to continue network activities initiated by your app even if the app is not actively running. This is particularly useful for tasks like downloading large files or syncing data with a server in the background.

You need to provide a unique identifier for the configuration object and set the isDiscretionary property to indicate whether the transfers can be deferred until optimal network conditions are met.

import UIKit

class MyViewController: UIViewController, URLSessionDelegate {

    // ...

    func startDownload() {
        // 1. Create background session configuration
        let configuration = URLSessionConfiguration.background(withIdentifier: "myBackgroundSession")
        configuration.isDiscretionary = true  // Optional for non-critical tasks
        configuration.sessionSendsLaunchEvents = true  // To receive events when tasks finish

        // 2. Create URLSession with the configuration
        let session = URLSession(configuration: configuration, delegate: self, delegateQueue: nil)

        // 3. Create and initiate the download task
        let downloadTask = session.downloadTask(with: url)
        downloadTask.resume()
    }

    // MARK: - URLSessionDelegate Methods

    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
        // Handle completed download (e.g., move file, update UI)
    }

    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
        // Handle task completion or errors
    }

    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
        // Track download progress (e.g., update progress bar)
    }
}

It’s important to consider the specific requirements of your tasks and choose the appropriate session type and configuration accordingly.