How to determine that NWBrowser has finished?

I am using NWBrowser to detect SignalK servers on a network using the following Swift code:

let browser = NWBrowser(for: .bonjourWithTXTRecord(type: "_http._tcp", domain: nil), using: NWParameters())
		
browser.browseResultsChangedHandler = { results, changes in
		print("Found \(results.count) results and \(changes.count) changes")
}

When this is run on a network with 5 devices then the output is often

  Found 5 results and 5 changes

But, sometime it is:

 	Found 2 results and 2 changes
	Found 5 results and 3 changes

indicating that the browseResultsChangedHandler is being called more than once.

So my question is how do I determine when the browsing process has finished (obviously without the knowledge that there are 5 devices)?

The depreciated NetServiceBrowser had a delegate method (netServiceBrowser(_:didFind:moreComing:) but I can't see an equivalent for NWBrowser.

The only method I can think of is to apply a short time out.

Answered by DTS Engineer in 791403022

Bonjour has no concept of a browse operation being ‘finished’. A browse will continue to send you updates as long as you leave it running. A browse only finishes when you cancel it.

The [deprecated] NetServiceBrowser had a [moreComing flag]

That flag doesn’t do what you think it does. Here’s what happens with the old API:

  1. The Bonjour daemon receives a browse response packet with multiple answers [1].

  2. It sends the first answer to your app. It can only send one answer at a time, so it sets the ‘more coming’ flag.

  3. It continues to do this until the last answer.

  4. For the last answer it clears the ‘more coming’ flag.

The intention is that your app can use this flag to defer updates, so that the list of services you show to the user doesn’t refresh a bazillion times while the initial set of results is coming in.

NWBrowser doesn’t have this problem because it can deliver answers to you en masse.

The only method I can think of is to apply a short time out.

That’s rarely the right option when it comes to Bonjour. What are you actually doing with these browse results?

Most folks display them in a UI, so there’s no need for a timeout. Rather, you cancel the browse when the user moves away from that UI.

Share and Enjoy

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

[1] Well, more likely it has these answers cached, but the story is the same either way.

Accepted Answer

Bonjour has no concept of a browse operation being ‘finished’. A browse will continue to send you updates as long as you leave it running. A browse only finishes when you cancel it.

The [deprecated] NetServiceBrowser had a [moreComing flag]

That flag doesn’t do what you think it does. Here’s what happens with the old API:

  1. The Bonjour daemon receives a browse response packet with multiple answers [1].

  2. It sends the first answer to your app. It can only send one answer at a time, so it sets the ‘more coming’ flag.

  3. It continues to do this until the last answer.

  4. For the last answer it clears the ‘more coming’ flag.

The intention is that your app can use this flag to defer updates, so that the list of services you show to the user doesn’t refresh a bazillion times while the initial set of results is coming in.

NWBrowser doesn’t have this problem because it can deliver answers to you en masse.

The only method I can think of is to apply a short time out.

That’s rarely the right option when it comes to Bonjour. What are you actually doing with these browse results?

Most folks display them in a UI, so there’s no need for a timeout. Rather, you cancel the browse when the user moves away from that UI.

Share and Enjoy

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

[1] Well, more likely it has these answers cached, but the story is the same either way.

Thanks Quinn, that's very helpful.

My application does a one-off search for SignalK servers at the start of a session. The user then selects one of them and sticks with it. If there are NO servers on the network, then I wanted to present that information to the user.

In practice, if the code has not discovered any after a few seconds, then I think its safe to say the search has finished (even if the browser is still running)

My general advice on this front is that you adjust your workflow to let the user be in change of the timeout. So, present an empty list (“Searching…”) and have a button that says “Continue without a server”.

As a user, it’s very frustrating when an app apply a timeout that’s just a little too short. Consider this scenario:

  1. I start a session.

  2. It stalls.

  3. Weird. Looking around, I notice the server in unplugged.

  4. I plug it in.

  5. I go back to my Mac and, blah, the app has timed out and I have to repeat step 1.

Share and Enjoy

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

How to determine that NWBrowser has finished?
 
 
Q