Can you help me understand the difference between a NetworkExtension.NWEndpoint and Network.NWEndpoint and where I would need to use one versus the other?
Sure. And that is, indeed, a gnarly edge case.
NetworkExtension.NWEndpoint
is an Objective-C class. It was introduced prior to the introduction of Network framework proper. Network framework is a Swift API [1] and, in Swift, it makes sense to represent endpoints as an enum, so Network.NWEndpoint
is an enum.
Quoting TN3151 Choosing the right networking API:
Network Extension in-provider networking includes
NWUDPSession
. While there are some very limited
circumstances where this is still useful, in most cases it’s
better to use Network framework. For more details, see
In-Provider Networking.
So, what are those circumstances? In short:
-
If your product supports system prior to the introduction of Network framework, that is, macOS 10.14. That’d be pretty unusual these days.
-
There are a few Network Extension APIs that work in terms of NetworkExtension.NWEndpoint
. In macOS 15 we added parallel mechanisms to that support Network.NWEndpoint
. If you support systems prior to that, you’ll need to either use the in-provider network APIs or implement some sort of shim to get things working on top of Network framework.
The additional of these parallel mechanisms was tricky, involving some exciting use of the .apinotes
file [2].
I’m trying to open a UDP listener on this endpoint
Wha? I can’t see any circumstances where that’d be necessary.
I need to copy datagrams between a provider flow object and an actual UDP socket or “connection”.
Doing that with NWConnection
is tricky due to a semantic disparity. There isn’t necessarily a one-to-one mapping between NEAppProxyUDPFlow
and NWConnection
because:
-
NEAppProxyUDPFlow
can represents a single local endpoint that sends to multiple remote endpoints.
-
NWConnection
represents a UDP flow, that is, a series of datagrams that all share the same local IP / local port / remote IP / remote port tuple
I see two paths forward:
-
Use a different API. Notably, BSD Sockets supports the same model as NEAppProxyUDPFlow
.
-
Create a different NWConnection
for each remote endpoint.
I’m not entirely sure whether the NWConnection
option will work though, because I don’t see a good way to create multiple outgoing connections with the same local port [3]. I’m still digging into that.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
[1] It also has a low-level C API.
[2] Below is an explanation of this I wrote in another context. It talks about NWPath
, not NWEndpoint
, but the same process applies for both.
In Swift 6 mode, NetworkExtension.NWPath
is published as NetworkExtension.__NWPath
so that Network.NWPath
can take priority. It does this by API notes. Specifically, NetworkExtension.framework/Versions/A/Headers/NetworkExtension.apinotes
has this:
…
- Name: NWPath
SwiftPrivate: true
…
SwiftVersions:
- Version: 5.0
…
- Name: NWPath
SwiftPrivate: false
…
In the Swift 6 language mode, SwiftPrivate
is true and thus the type gets published as __NWPath
. In Swift 5 language mode, SwiftPrivate
is false and thus it continues to be published as NWPath
.
[3] The obvious option, setting requiredLocalEndpoint
, just generates an EADDRINUSE
, at least in my case.