Network.framework Swift example

I'm working with the Network.framework in a Network Extension and so far it seems the documentation is very imature. I opened a rdar about it but I was wondering if there's a swift example published that uses the Network.framework.


My main concern right now is lack of documentation in:


NWConnection.stateUpdateHandler: In some cases I see a connection stuck in the waiting state with the error ENETDOWN. So a canonical way to handle the .waiting state would be useful.


For the NWConnection.receive I see sometimes the the completion function receiving an Empty data without any error. And that causes all the subsequent calls to receive to return the same. I didn't find in the docs how to handle failures in the `receive`. From the WWDC talk it seemed it should be reflected in the `stateUpdateHandler`.


For the NWConnection.receive I see sometimes the the completion function receiving an Empty data WITH an error. And that causes all the subsequent calls to receive to return the same. I didn't find in the docs how to handle failures in the `receive`. From the WWDC talk it seemed it should be reflected in the `stateUpdateHandler`.

Accepted Reply

I'm not the only one … having a hard time finding documentation on the Network.framework. Shall I open a rdar asking for more examples?

Absolutely. Please post the bug number, just for the record.

Do you know by any chance if there's a documentation on what conditions make a connection to go into

.waiting
state?

There is not. Documenting that would be hard because it’s all about implementation details. Lurking underneath the Network framework API is a complex chunk of code that attempts to open connections [1]. The

.waiting
state means that this connection engine has got to a point where it’s exhausted all available options. It’s now waiting for some visible change to the network environment in order to retry.

Remember that visible means visible to the local device. Thus, it’s relatively easy to get into a position where a connection is in the

.waiting
state but doing an immediate retry will succeed. For example:
  1. Set up a server located across the wider Internet.

  2. Unplug its Ethernet cable.

  3. Start a connection on the client; it can’t connect to the server and thus enters the

    .waiting
    state.
  4. Plug the server’s Ethernet cable back in.

  5. The client will stay in the

    .waiting
    state because the change in step 4 is not visible to it.

The way I approach problems like this depends on the nature of the connection:

  • Solicited network connections are those directly requested by the user [2].

  • Unsolicited network connections are not directly connected to a user action.

For a solicited network connection, it’s best to include a UI that indicates progress and allows the user to cancel and retry. This has two key advantages. First, it puts the user in control, which users really like. Second, it avoids you having to make any policy decisions, which is good because it’s hard to come up with a policy that works in all scenarios.

For example, consider the scenario described above. The best way to get the connection through is for the user to cancel and retry. OTOH, if the connection failed to go through because the user has stepped out of Wi-Fi range, the best fix is for them to step back into range.

For unsolicited connections, assistance from the user is not an option and thus you have to come up with a retry policy. Personally I’m a big fan of leaning on the built-in policy from the Network framework, but there’s nothing stopping you from building on top of that.

The funny thing is that after that it immediately goes to ready.

In situations like this it’s worth evaluating whether the connection engine should have retried. If the problem that prevented the first connection going through was local — that is, visible to the client device as I’ve discussed above — and the problem cleared but the connection is still waiting, that’s definitely bugworthy.

If you file a bug about this:

  • Make sure to include a sysdiagnose log, as described on our Bug Reporting > Profiles and Logs page.

  • Make sure to note, with some degree of precision, the time that you started the first connection and the time that you started the second.

    The best way to do this is to add your own entry to the system log when you start a connection. Using

    NSLog
    with an easily identifiable log string should be sufficient.
  • Again, please post your bug number, just for the record.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

[1] For an inkling of how complex this is, consider RFC 8305 and then realise that the Apple implementation is much more complex (-:

[2] Here I’m using terms from Technote 1145 Living in a Dynamic TCP/IP Environment, one of my oldest technotes that I regularly reference!

Replies

NWConnection.stateUpdateHandler
: In some cases I see a connection stuck in the waiting state with the error
ENETDOWN
. So a canonical way to handle the
.waiting
state would be useful.

You really have two choices here:

  • You can either allow the connection to continue waiting, hoping that it will connect once network conditions improve.

  • You can cancel the connection, perhaps after some timeout.

Which you do really depends on the nature of your connection. For a GUI app my general recommendation is that you wait. If this connection is the result of an operation that the user specifically requested, you should show the user the status of the operation and let them cancel if they want.

In other situations it might make more sense to cancel the connection programmatically. For example, if you’re building a command-line tool on the Mac, you don’t want to wait indefinitely for a connection.

For the

NWConnection.receive
I see sometimes the the completion function receiving an Empty data without any error.

That sounds like an end-of-stream indication. Is the

isComplete
Boolean passed to your completion handle set to true?

If so, you should process any data you got (in this case it’s empty but that may not always be the case) and then handle the end-of-stream. Typically this involves shutting the connection down (cancel the connection, clean up your state, indicate the end-of-stream to your client).

For the

NWConnection.receive
I see sometimes the the completion function receiving an Empty data WITH an error.

Right. You should process any data you got (again, while you’re seeing it as empty right now, it may not always be) and then handle the error in much the same way as an end-of-stream.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

>

> You really have two choices here:

> - You can either allow the connection to continue waiting, hoping that it will connect once network conditions improve.

> - You can cancel the connection, perhaps after some timeout.


I'm cancelling and starting a new connection. The funny thing is that after that it immediately goes to ready. I don't know why there are some conditions where it never leaves the .waiting state.


Do you know by any chance if there's a documentation on what conditions make a connection to go into .waiting state?


Also I think I'm not the only one (https://forums.developer.apple.com/message/350013#350013) having a hard time finding documentation on the Network.framework. Shall I open a rdar asking for more examples?

I'm not the only one … having a hard time finding documentation on the Network.framework. Shall I open a rdar asking for more examples?

Absolutely. Please post the bug number, just for the record.

Do you know by any chance if there's a documentation on what conditions make a connection to go into

.waiting
state?

There is not. Documenting that would be hard because it’s all about implementation details. Lurking underneath the Network framework API is a complex chunk of code that attempts to open connections [1]. The

.waiting
state means that this connection engine has got to a point where it’s exhausted all available options. It’s now waiting for some visible change to the network environment in order to retry.

Remember that visible means visible to the local device. Thus, it’s relatively easy to get into a position where a connection is in the

.waiting
state but doing an immediate retry will succeed. For example:
  1. Set up a server located across the wider Internet.

  2. Unplug its Ethernet cable.

  3. Start a connection on the client; it can’t connect to the server and thus enters the

    .waiting
    state.
  4. Plug the server’s Ethernet cable back in.

  5. The client will stay in the

    .waiting
    state because the change in step 4 is not visible to it.

The way I approach problems like this depends on the nature of the connection:

  • Solicited network connections are those directly requested by the user [2].

  • Unsolicited network connections are not directly connected to a user action.

For a solicited network connection, it’s best to include a UI that indicates progress and allows the user to cancel and retry. This has two key advantages. First, it puts the user in control, which users really like. Second, it avoids you having to make any policy decisions, which is good because it’s hard to come up with a policy that works in all scenarios.

For example, consider the scenario described above. The best way to get the connection through is for the user to cancel and retry. OTOH, if the connection failed to go through because the user has stepped out of Wi-Fi range, the best fix is for them to step back into range.

For unsolicited connections, assistance from the user is not an option and thus you have to come up with a retry policy. Personally I’m a big fan of leaning on the built-in policy from the Network framework, but there’s nothing stopping you from building on top of that.

The funny thing is that after that it immediately goes to ready.

In situations like this it’s worth evaluating whether the connection engine should have retried. If the problem that prevented the first connection going through was local — that is, visible to the client device as I’ve discussed above — and the problem cleared but the connection is still waiting, that’s definitely bugworthy.

If you file a bug about this:

  • Make sure to include a sysdiagnose log, as described on our Bug Reporting > Profiles and Logs page.

  • Make sure to note, with some degree of precision, the time that you started the first connection and the time that you started the second.

    The best way to do this is to add your own entry to the system log when you start a connection. Using

    NSLog
    with an easily identifiable log string should be sufficient.
  • Again, please post your bug number, just for the record.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

[1] For an inkling of how complex this is, consider RFC 8305 and then realise that the Apple implementation is much more complex (-:

[2] Here I’m using terms from Technote 1145 Living in a Dynamic TCP/IP Environment, one of my oldest technotes that I regularly reference!