Contents

TN3102: HTTP/3 in your app

Get started with iOS 15’s new HTTP/3 support.

Overview

HTTP/3 is a major new feature in iOS 15. While you wait for your server to add support for it, here’s how you can test it today in your app:

  1. Make sure your test device is running iOS 15 or later (HTTP/3 is not supported in the simulator).

  2. In Settings > Developer, under the Networking group, enable HTTP/3.

  3. Using Xcode 13.0 or later, create a new project from the iOS > App template. Set the Language popup to Swift and the Interface popup to SwiftUI.

  4. Change the HTTP3App.swift and the ContentView.swift files to the code shown below.

  5. Run the app on your device.

  6. Tap your Test button.


// HTTP3App.swift

@main
struct HTTP3App: App {
    let networkManager = NetworkManager()
    var body: some Scene {
        WindowGroup {
            ContentView(networkManager: networkManager)
        }
    }
}

// ContentView.swift

import SwiftUI
import os.log

struct ContentView: View {
    var networkManager: NetworkManager
    
    var body: some View {
        VStack(alignment: .leading) {
            Button("Test HTTP3") {
                networkManager.testHTTP3Request()
            }.padding(.vertical, 20)
        }.padding(40)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

class NetworkManager: NSObject, URLSessionDataDelegate {
    
    private var session: URLSession!
    
    func testHTTP3Request() {
        
        if self.session == nil {
            let config = URLSessionConfiguration.default
            config.requestCachePolicy = .reloadIgnoringLocalCacheData
            self.session = URLSession(configuration: config, delegate: self, delegateQueue: .main)
        }
        
        let urlStr = "https://google.com"
        let url = URL(string: urlStr)!
        var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 60.0)
        request.assumesHTTP3Capable = true
        os_log("task will start, url: \(url.absoluteString)")
        self.session.dataTask(with: request) { (data, response, error) in
            if let error = error as NSError? {
                os_log("task transport error \(error.domain) / \(error.code)")
                return
            }
            guard let data = data, let response = response as? HTTPURLResponse else {
                os_log("task response is invalid")
                return
            }

            guard 200 ..< 300 ~= response.statusCode else {
                print("task response status code is invalid; received \(response.statusCode), but expected 2xx")
                return
            }
            os_log("task finished with status \(response.statusCode), bytes \(data.count)")
        }.resume()
        
    }
}

extension NetworkManager {
    
    func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) {
        let protocols = metrics.transactionMetrics.map { $0.networkProtocolName ?? "-" }
        os_log("protocols: \(protocols)")
    }
}

Testing notes

After running the code above, your app will print something similar to:

task will start, url: <# Your H3 URL Here #>
protocols: ["h3-29"] // ["h3", "h2"]
task finished with status 200, bytes 703

The protocols array shows the protocol used for each HTTP transaction. In this case there was a single HTTP/3 transaction. In some cases you may see the protocol being returned as h2 or even http1.1. If this is the case then run the transaction again. This gives URLSession the opportunity to apply the HTTP/3 service discovery option that your server sent back on the previous HTTP response.

Service discovery for HTTP/3 is performed in one of the following ways:

  • The recommended approach is to configure your DNS server to advertise the HTTPS resource record for alpn="h3,h2".

  • Alternatively, configure your server to respond back with the Alt-Svc header that advertises HTTP/3. For example, Alt-Srv: h3=":443"; ma=2592000.

To attempt to use HTTP/3 on the first transaction, set the assumesHTTP3Capable property on the URLRequest being used to execute the data task in URLSession.

For cases where HTTP/3 is not supported on the server, or over the network, you will see the protocol fall back to h2 or http1.1. For more information on this, please see the WWDC session for Accelerate networking with HTTP/3 and QUIC.

If you experience issues while debugging your network transactions, take a look at these requests in the Network Instruments tool to debug what you are seeing being sent back from your server. For more on this, see the article for Analyzing HTTP Traffic with Instruments.

Revision History

  • 2022-05-24 Made minor editorial changes.

  • 2022-02-08 Republished as TN3102.

  • 2021-06-16 First published as “HTTP/3 in Your App” on the Apple Developer Forums.

See Also

Latest