[Accepted] Vision: A Vision for Networking in Swift

Hello, Swift Community,

I'm pleased to announce that the Ecosystem Steering Group has accepted a vision document titled A Vision for Networking in Swift.

Today, Swift offers multiple networking APIs with overlapping capabilities. Developers who aren't networking experts must determine which tools fit their problem, often with minimal guidance. A suboptimal choice leads to platform-support gaps, missing features, and costly rewrites.
...

This document proposes a vision for networking in Swift that addresses fragmentation and embraces modern Swift. We outline goals, describe design considerations that guide the solution space, and present an approach.

Goals

  • High-level and safe by default. Most developers should use network clients and servers without worrying about underlying complexity. Structured concurrency is the natural programming model by design, secure defaults require no configuration, and disabling safeguards for advanced use cases must be possible but explicit.
  • Modular and interoperable. Currency types enable libraries to interoperate without coupling to specific implementations. Network protocols and I/O backends should be independently replaceable, with platform-specific complexity pushed to lower layers.
  • Excellent everywhere. Cross-platform code should work transparently across diverse environments with minimal latency and maximum throughput: from servers handling millions of concurrent connections to memory-constrained embedded runtimes, to devices optimizing for power, to Wasm in browsers and plugins. The same abstractions should enable customisations to leverage the unique characteristics of each environment while retaining a common programming model.
  • Integrated and observable. Observability via logging, metrics, tracing, and protocol-level diagnostics, should be available at all layers. Users of existing solutions have clear migration paths, and other elements of the ecosystem like Swift Configuration and Swift Service Lifecycle integrate well.

This document, written by @ekinnear and @Lukasa, lays out a vision for the future of Networking in Swift.

As discussed in the Language Steering Group's general post about vision documents, the Ecosystem Steering Group's acceptance of this document is a strong endorsement of the goals laid out in the vision, a general endorsement of the basic approach, but only a weak endorsement of any concrete proposals. All proposals in the vision will have to undergo ordinary evolution review, which may result in rejection or major revision.

Please feel free to continue discussing this vision document in this thread.

Franz Busch,
Ecosystem Steering Group

35 Likes

I meant to post when this was first announced. I think the document is improved over the original version, though it seems even more non-specific (or perhaps just covering a broader subject) than virtually all other vision documents, so I don't find it providing much real guidance.

Really my only remaining issues are with the FAQ.

Is the long-term vision to phase out SwiftNIO?

No. We're unifying the underlying primitives across SwiftNIO, Network.framework, URLSession, and others to reduce duplication and increase cross-platform consistency.

Is this even possible? These frameworks work completely differently, so it's not really possible to unify their underlying primitives, unless you're only talking about things like HTTPHeader or something. NIO's concurrency and data handling are completely different from URLSession's, and both expose different amounts of their lower level implementation. Network.framework is a C framework, so are we talking about exposing a C version of the new stack? Won't that radically limit the implementation?

And at the same time, this vision seems to outline a top to bottom Swift stack that would be completely separate from existing frameworks. So are we somehow committing to a completely new stack and rebuilding every existing network framework to use it? How is that a better idea than just keeping them separate?

Will Network.framework and SwiftNIO converge?

Yes. The proposal is that, over time, both will be replaced by a new unified networking stack, where NIO and Network.framework, become, if anything, a smaller implementation detail for cross-platform support.

So are we phasing out NIO and replacing it with a new unified network stack, or are we building a new network stack and refactoring NIO to be built on top of it? These first two answers seem rather contradictory. NIO's base abstractions are rather radically different than a modern Swift stack would use, so to actually achieve this goal, wouldn't the changes be so breaking that it would be a new framework anyway? Or is the goal to literally rebuild things like ByteBuffer in such a way as to seamlessly pass through whatever's used for the Swift stack? (Some kind of span?)

This seems like a lot of extra effort, and I would hate to see any compromise in the new stack because of it.

Will the proposed changes be backward compatible?

Existing libraries and frameworks can adopt the new types incrementally. For example, URLSession already supports HTTPRequest and HTTPResponse alongside URLRequest and URLResponse.

IIRC, URLSession does not support these types. Instead, the swift-http-types library provides compatibility shims between the new types and Foundation in HTTPTypesFoundation. However, these sorts of translation layers aren't a long term solution, and are a natural performance bottleneck and abstraction break for users. So really the answer to this question is no, there will be no backward compatibility, because the new stack is entirely new. Instead, there may be limited compatibility shims to allow developers to unify certain representations, at a cost.

And obviously this FAQ doesn't address the unanswerable elephant in the room: will Apple ship this new network stack as part of its OSes, will they make their other APIs compatible with it, or will they phase out the older stacks? From this FAQ it seems like the implicit answer is no, and the intent is to rebuild Apple's networking APIs on top of this new library. Which is unfortunate, given the limitations of that approach, but as long as that doesn't limit the ability to use the stack as a package, could be doable. Just seems like a whole lot of extra work.

So while I look forward to this new work, I hope it's allowed to breathe and evolve before being locked inside existing solutions and unable to change.

10 Likes

I can hopefully answer some of these questions with both my ESG and my Vapor hat on.

To start, this is just a vision document setting out high level goals. There are some specifics in there around the different current parts but the implementation and API is still very much TBD.

The goal is to provide a unified API for any Swift user to use for networking. (I'm focusing on HTTP clients here primarily but the same applies for the server). So when you're writing a Swift program, be that an iOS app, a CLI on Linux or code for wasm, you have the same API to use. There is no confusion about whether you need to pick URLSession/AsyncHTTPClient/SwiftCurl etc, you reach for HTTPClient (not the final name) on any and all platforms. These are APIs and the implementation can be different per platform. So the underlying driver might be URLSession on Apple Platforms, AHC on Linux, fetch() on wasm, but that's an implementation detail unless you want to reach for it. This work has already begun at GitHub - apple/swift-http-api-proposal: This repository contains a proposal for standardized HTTP client and server APIs for the Swift ecosystem · GitHub. So in my mind, it's a brand new API that existing stacks can be an implementation for. Currently, yes they all have different nuances and capabilities, but one of the goals of the vision is to unify them. And from an ESG point of view I personally don't think we should be tied to any existing API or implementation just because it already exists and is there. We should build the best API we can with the modern Swift features we have available to us now, not clinging on to something that was built in Objective-C 20 years ago.

With regards to how these will be shipped, there's general agreement that these have to be packages. They cannot be shipped as part of the toolchain, otherwise security fixes will take too long to land. There's work that needs to be done to make this as easy as possible for different platforms and SDKs but it's something we're looking into.

4 Likes

These points seem contradictory. How could we possibly build the best API if it has to be implementable using decades old Obj-C types? You can wrap URLSession up however you want, it will never actually be modern. I want to be able to reach all the way down the stack, I can't do that if URLSession is in the way.

So is the goal to actually build a full Swift networking stack, or not? If so, why then also reimplement parts of it on mismatched foundations? Building some platform specific abstractions, when needed, since the basic networking looks and works differently on different platforms, makes sense. I even hope we see a modern version of urlsessiond to replace the current background handling, but I also hope it's new code. I suppose, as long as it's an option to use the fully native stack, having alternate foundations on the different platforms isn't a huge issue, it just seems rather wasteful.

2 Likes

Could you expand on this, maybe showing a few examples? Why does URLSession not feel modern to you? What particular Obj-C types you are talking about? What do you want to reach at the bottom of the URLSession stack?

Let me try to add to the points that @0xTim already made. We, the Ecosystem Steering Group, want to see a new networking stack that is native to Swift and Swift Concurrency including new HTTP APIs. This new networking stack and the HTTP APIs should in no-way be tied to any APIs of the existing implementation; however, we want to acknowledge that:

  1. Any new implementation should learn from existing implementations to produce something that is better in all possible dimensions.
  2. Any existing implementation is expected to stay around. Network.framewok, SwiftNIO, URLSession and AsyncHTTPClient have large existing user bases that rely on them.
  3. If possible an interoperability between existing and new solutions should be created to help with migrating the ecosystem.

We have asked the vision document authors to add a diagram to illustrate the high level architecture. (Replicated here)

As you can see, the new proposed architecture here is a lot more decoupled than how both NIO and Network.framework are currently built. There are clear delineations between the individual building blocks. The FAQ item that you quoted is trying to articulate that some of those building blocks exist in one or both of those frameworks already and can be extracted, decoupled, and then re-used by both existing and entirely new solutions.

As a concrete example, swift-nio-http2 provides a pure Swift implementation of the HTTP/2 RFC. However, this implementation is tightly coupled to NIO. Refactoring the HTTP/2 state machine into a new swift-http2 package that is purely built on top of currency types from the Swift standard library, swift-http-types, and a hypothetical new swift-network-types will allow us to integrate it into the new networking stack while also allowing us to relayer swift-nio-http2 on top of it. This way, both existing users of NIO and users of the new stack share the benefits.

Now there are a few technical challenges that we need to overcome such as defining the right currency types and figuring out how we could avoid a new major version for swift-nio-http2 when relayering but those are details that need to go into a proposal not into a vision document.

13 Likes

I like the direction of this vision, especially the emphasis on modularity, currency types, clear layering, and moving platform-specific complexity lower in the stack.

My perspective comes from a long history of IP network architecture and design work, particularly in carrier, public-sector, and operational infrastructure environments. From that angle, I think this vision can open an even larger opportunity for Swift than client/server APIs alone: Swift can become a strong language for network control, infrastructure modeling, routing/security tooling, diagnostics, automation, and policy systems.

That opportunity depends heavily on getting the lower currency-type layer right.

One point I would like to reinforce is that the core IP currency layer probably should not stop at IPAddress.

For many applications, an address is the obvious primitive. But in routing, ACLs, interface configuration, RPKI validation, IPAM, DNS operations, peer configuration, summarization, diagnostics, multicast handling, and network automation, the unit of work is often not just “an address”. It is frequently an address with prefix context, a canonical network prefix, a delegated block, or a neutral CIDR range.

A useful IP currency model for those domains likely needs concepts along these lines:

AddressFamily
IPAddress<Family>
PrefixLength<Family>
IPNetwork<Family>
CIDRBlock<Family>
IPMulticastGroup<Family>

The important part is not those exact names. The important part is preserving the semantic boundaries.

For example:

  • IPAddress can represent concrete address bits with address-family and prefix context.
  • PrefixLength can validate prefix lengths per address family.
  • IPNetwork can represent a canonical network boundary.
  • CIDRBlock can represent a neutral prefix-aligned range or delegation form.
  • IPMulticastGroup can preserve multicast group semantics instead of inheriting unicast host/subnet/broadcast assumptions.
  • AddressFamily can be a compile-time trait rather than a runtime tag, keeping IPv4 and IPv6 operations type-safe while still allowing mixed-family wrappers at API boundaries.

Those values are related, but they are not interchangeable. In this model, even an address-only spelling has prefix context. For IPv4, 192.0.2.1 is equivalent to the host address form 192.0.2.1/32. The difference is whether the caller needs to surface that prefix context or only access the address value.

192.0.2.1                   // IPAddress — host address form; implied /32 for IPv4
192.0.2.1/24                // IPAddress — address with prefix context; host bits preserved
192.0.2.0/24                // IPNetwork — canonical network prefix; host bits zeroed
192.0.2.0/24 as delegated   // CIDRBlock — neutral delegated range/block
224.0.0.251                 // IPMulticastGroup — multicast group address; implied /32 for IPv4

Those examples may share address-family, parsing, formatting, and bit-manipulation machinery, but they have different meanings in real network software. Collapsing them too early into strings, socket addresses, or one generic address type makes higher-level code recreate the missing semantics repeatedly.

As one implementation data point, I have been exploring this shape in swift-cidr, a pure Swift CIDR math and IP currency-type package. The goal of that package is not to model the full transport layer or socket metadata. Its primary focus is CIDR-safe address-family modeling, prefix validation, canonical network calculation, containment, range/delegation semantics, multicast group/range modeling, and related IP math.

That experience has made one design boundary stand out: SocketAddress should probably be treated as an adapter/platform representation, not the central semantic IP type.

A socket address is important, but it carries platform and transport representation concerns. The CIDR layer should be able to exist below that. It should provide correct IP address, prefix, network, range, and family semantics without being shaped by sockaddr, NIO channel types, or backend-specific storage.

Another module can absolutely build the next layer: endpoint values, transport protocol metadata, socket adapters, SwiftNIO integration, Network.framework integration, and higher-level connection APIs. But those layers become cleaner if they can build on a small, correct, shared CIDR foundation instead of each reinventing address and prefix behavior independently.

Similarly, an address-only view of IPAddress is useful, but it is not enough for the systems side of networking. Operations such as containment, subnet traversal, summarization, prefix-range matching, multicast group-range validation, and policy modeling naturally belong to a network-aware model. If those concepts are left to each package to reinvent, Swift risks repeating fragmentation at the IP layer that this vision is trying to reduce at higher networking layers.

So my main feedback is:

The vision’s currency-type direction is right. I think the foundational IP layer should be broad enough to cover real infrastructure work: addresses, prefix lengths, canonical network prefixes, neutral CIDR ranges/delegations, multicast group/range semantics, and clear adapter boundaries for POSIX/NIO/platform interop.

That would give Swift packages a stable vocabulary for addresses and prefixes across configuration files, JSON APIs, persistence, routing/security tools, server frameworks, diagnostics, and network automation without forcing every package to rebuild CIDR semantics on top of strings or socket-shaped types.

5 Likes