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:
Make sure your test device is running iOS 15 or later (
HTTP/3is not supported in the simulator).In Settings > Developer, under the Networking group, enable
HTTP/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.
Change the
HTTP3App.swiftand theContentView.swiftfiles to the code shown below.Run the app on your device.
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 703The 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-Svcheader that advertisesHTTP/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
TN3205: Low-latency communication with RDMA over ThunderboltTN3206: Updating Apple Pay certificatesTN3179: Understanding local network privacyTN3190: USB audio device design considerationsTN3194: Handling account deletions and revoking tokens for Sign in with AppleTN3193: Managing the on-device foundation model’s context windowTN3115: Bluetooth State Restoration app relaunch rulesTN3192: Migrating your iPad app from the deprecated UIRequiresFullScreen keyTN3151: Choosing the right networking APITN3111: iOS Wi-Fi API overviewTN3191: IMAP extensions supported by Mail for iOS, iPadOS, and visionOSTN3134: Network Extension provider deploymentTN3189: Managing Mail background traffic loadTN3187: Migrating to the UIKit scene-based life cycleTN3188: Troubleshooting In-App Purchases availability in the App Store