Combining Bonjour and QUIC multiplex group using Network.framework

In my iOS app I am currently using Bonjour (via Network.framework) to have two local devices find each other and then establish a single bidirectional QUIC connection between them.

I am now trying to transition from a single QUIC connection to a QUIC multiplex group (NWMultiplexGroup) with multiple QUIC streams sharing a single tunnel.

However I am hitting an error when trying to establish the NWConnectionGroup tunnel to the endpoint discovered via Bonjour. I am using the same "_aircam._udp" Bonjour service name I used before (for the single connection) and am getting the following error:

nw_group_descriptor_allows_endpoint Endpoint iPhone15Pro._aircam._udp.local. is of invalid type for multiplex group

Does NWConnectionGroup not support connecting to Bonjour endpoints? Or do I need a different service name string? Or is there something else I could be doing wrong?

If connecting to Bonjour endpoints isn't supported, I assume I'll have to work around this by first resolving the discovered endpoint using Quinn's code from this thread? And I guess I would then have to have two NWListeners, one just for Bonjour discovery and one listening on a port of my choice for the multiplex tunnel connection?

Does NWConnectionGroup not support connecting to Bonjour endpoints?

I don’t have an immediate answer to that, but I’d like you to run a test. If you switch to a .hostPort(…) endpoint, does everything work?

You don’t need to do the full resolve thing to test this, just hard code the service’s local DNS name. For example, if the service name is Guy Smiley then the local DNS name is gonna be guy-smiley.local..

The point of this test is that it’ll determine you whether this issue is specific to your use of a .service(…) endpoint, and not some other oddity. Oh, and if the .hostPort(…) endpoint works then that’s good evidence that your proposed workaround is feasible.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thank you for the quick reply.

Unfortunately I don't quite understand how I would do this. I assume I would still use a NWListener with a NWListener.Service to advertise my service over Bonjour? But even if I don't need to resolve the hostname and instead hardcode it (in my case as "_aircam._udp.local."), I'd still need to specify a port to use .hostPort(), right? And NWListener doesn't allow me to choose my own port as far as I can tell.

I assume nw_group_descriptor_allows_endpoint is a closed source function?

I did (try to) implement the workaround of using Bonjour just for device discovery, resolving the hostname and then trying to establish a QUIC tunnel (multiplexed NWConnectionGroup) using that hostname and a hardcoded port. Unfortunately I did not get very far before hitting another wall.

Creating the NWConnectionGroup using my QUIC parameters and a .hostPort() endpoint worked without getting an error. On the other device I am creating a NWListener with QUIC parameters and the hardcoded port. Its newConnectionGroupHandler is called, and after accepting it (by calling start(queue:..)) the NWConnectionGroups on both devices enter the .ready state. Great!! 🥳

Next I am getting a QUIC stream (NWConnection) from the group by calling .extract(), and call .start(queue:..) on it. Unfortunately this is where I hit a wall again. The logs show the following errors:

nw_endpoint_flow_setup_cloned_protocols [C4 fe80::1c46:3f2a:b0a6:c276%en0.7934@en0 in_progress channel-flow (satisfied (Path is satisfied), interface: en0[802.11], scoped, ipv4, ipv6, dns, uses wifi)] could not find protocol to join in existing protocol stack

nw_endpoint_flow_failed_with_error [C4 fe80::1c46:3f2a:b0a6:c276%en0.7934@en0 in_progress channel-flow (satisfied (Path is satisfied), interface: en0[802.11], scoped, ipv4, ipv6, dns, uses wifi)] failed to clone from flow, moving directly to failed state

and the connection ends up .cancelled.

The exact same error messages were being discussed in this older thread on QUIC multiplexed groups. However in that thread the issue ended up being different QUIC options being used for establishing the tunnel and the individual streams. In my case I am letting the stream inherit the QUIC options from the group/tunnel (though I also tried passing them in explicitly). I am using the same QUIC options that had been working for establishing a single NWConnection.

Hmmmm, I’m not sure why this is failing. I have a test project that uses a QUIC connection group with .hostPort(…) and I didn’t encounter this issue.

Hmmm, looking at my code I’m not using extract(connectionTo:using:). Rather, I’m creating a new connection with the NWConnection.init(from:to:using:) initialiser. Wanna give that a try?

ISTR that I’ve tested this from both the client and server side and it worked in both cases.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thank you, I tried it but it didn't make any difference.

However I just figured it out: The issue was that I was adding my custom protocol framer options into the NWParameters in addition to the QUIC options when establishing the tunnel / connection group. It seems that tripped up nw_endpoint_flow_setup_cloned_protocols.

Removing the custom framer allows me to now successfully create a NWConnection from the group.

Next I'll have to figure out how to get my custom framer added back into the individual NWConnections. It seems that even though the .parameters property is a constant, I can still modify it because it is a reference type. So hopefully that will work. 🤞

I filed enhancement request FB15647226 for the initial issue of NWConnectionGroup not supporting connections to Bonjour service endpoints.

Combining Bonjour and QUIC multiplex group using Network.framework
 
 
Q