[Pitch] New HTTP loader for URLSession on Darwin

Hi all, I'd like to share a proposal about the new HTTP loader for URLSession on Darwin. The API is not currently available non-Darwin platforms. Please check out the details below.


New HTTP loader for URLSession

Introduction

The new HTTP stack built in Network framework was enabled in Safari on iOS 18 and macOS Sequoia. It supports new features like 0-RTT early data, oblivious HTTP, WebSocket over HTTP/2, and unix domain socket. Support for FTP and HTTP/1 pipelining was dropped.

In this proposal, we are introducing an API to enable the new HTTP loader in URLSession on Darwin, as well as deprecating several legacy features not supported in the new stack.

Proposed solution

Introducing a new flag usesClassicLoadingMode that defaults to true on URLSessionConfiguration to allow developers to opt-in to the new stack.

The flag is not available on non-Darwin platforms at this moment.

Detailed design

Opt-in flag

The new loader is enabled when usesClassicLoadingMode is set to false. usesClassicLoadingMode currently defaults to true, but might default to false in a future OS.

open class URLSessionConfiguration {

    /* Uses the classic network loader */
    @available(*, unavailable, message: "Not available on non-Darwin platforms")
    open var usesClassicLoadingMode: Bool
}

Deprecations

  • FTP was deprecated in the old stack and unsupported in the new stack.
  • HTTP/1 pipelining is not supported in the new HTTP stack. It is disabled by default in the old stack and has known compatibility issues with some servers.
  • Server push was disabled in both stacks on iOS 17 and aligned releases due to low adoption and web compatibility issues.
  • NSURLErrorFailingURLStringErrorKey is redundant with NSURLErrorFailingURLErrorKey and it is incompatible with WHATWG URLs.
  • shouldUseExtendedBackgroundIdleMode was not supported in either the old stack or the new stack.
public struct URLRequest {

    @available(swift, deprecated: 6.1, message: "HTTP/1 pipelining has known compatibility issues, please adopt HTTP/2 and HTTP/3 instead")
    public var httpShouldUsePipelining: Bool
}

open class NSURLRequest {

    @available(swift, deprecated: 6.1, message: "HTTP/1 pipelining has known compatibility issues, please adopt HTTP/2 and HTTP/3 instead")
    open var httpShouldUsePipelining: Bool { get }
}

open class NSMutableURLRequest {

    @available(swift, deprecated: 6.1, message: "HTTP/1 pipelining has known compatibility issues, please adopt HTTP/2 and HTTP/3 instead")
    open override var httpShouldUsePipelining: Bool
}

open class URLSessionConfiguration {

    @available(swift, deprecated: 6.1, message: "HTTP/1 pipelining has known compatibility issues, please adopt HTTP/2 and HTTP/3 instead")
    open var httpShouldUsePipelining: Bool

    @available(swift, deprecated: 6.1, message: "Not supported")
    open var shouldUseExtendedBackgroundIdleMode: Bool
}

public enum URLSessionTaskMetrics.ResourceFetchType {

    @available(swift, deprecated: 6.1, message: "Server push is not supported")
    case serverPush
}

public struct URLError {

    @available(swift, deprecated: 6.1, message: "Use failingURL instead")
    public var failureURLString: String?
}

@available(swift, deprecated: 6.1, message: "Use NSURLErrorFailingURLErrorKey instead")
public let NSURLErrorFailingURLStringErrorKey: String

@available(swift, deprecated: 6.1, message: "FTP is deprecated")
public let NSURLProtectionSpaceFTP: String

@available(swift, deprecated: 6.1, message: "FTP is deprecated")
public let NSURLProtectionSpaceFTPProxy: String

Source compatibility

No impact.

Implications on adoption

This feature can be freely adopted and un-adopted in source code with no deployment constraints and without affecting source compatibility.

Future directions

The new HTTP stack might become the default on a future Darwin release.

Alternatives considered

Alternative spelling usesNewLoader

The initial idea was an opt-in flag where YES means the new loader, and we considered multiple variants, including usesModernLoader, usesNWLoader (NW stands for Network framework). However, this makes it hard to come up with names for newer loaders in the future, and an opt-out flag gives the impression that the new loader is the default, not the exception.

2 Likes

Is the implementation not open? I don't see it linked among any of the PRs. Or has it been merged somewhere already?

Currently, the implementation is Darwin-specific and not open source.

If it's possible that new loaders get introduced in the future, wouldn't it better to model it as a non-frozen enum rather than potentially multiple boolean properties? For example:

enum LoadingMode {
    case v1 // or "classic"
    case v2 // or "modern"
}

With the pitched design, we might run into situations like this:

session.usesClassicLoadingMode = false
// Imaginary name for the "modern" loader introduced with this pitch,
// once an even more modern loader has been introduced in, say, 2035
session.usesClassic2025LoadingMode = false

Including the presumably invalid state where someone sets more than one of theses properties to true.

4 Likes

Thank you Ole for bringing this up. Yes, we had the same discussion internally about whether to make this an enum. The bar for doing a behavior breaking rewrite is extremely high, and we do not plan to introduce another loading mode for the remaining lifetime of the URLSession API.

1 Like

Thank you all for your input. I'd like to accept this pitch as an abbreviated review as there's no open feedback. The proposed API addition and deprecation surface is also relatively small.

2 Likes