Use NWPathMonitor with Swift Modern Concurrency (AsyncStream) vs GCD (DispatchQueue)

776 views Asked by At

I have noticed that the start(queue:) method in NWPathMonitor requires a queue of type DispatchQueue. Is there a way to implement this using Swift Modern Concurrency, probably using AsyncStream?

Using Apple documentation for AsyncStream, I have created the extension to NWPathMonitor, but I cannot start the NWPathMonitor monitor, any suggestion will be appreciated, thanks

extension NWPathMonitor {
  static var nwpath: AsyncStream<NWPath> {
    AsyncStream { continuation in
      let monitor = NWPathMonitor()
      monitor.pathUpdateHandler = { path in
        continuation.yield(path)
      }
      continuation.onTermination = { @Sendable _ in
        monitor.cancel()
      }
      // monitor.start(queue: )
    }
  }
}

Read Apple's documentation

1

There are 1 answers

0
Rob On

If you are wrapping legacy APIs within some continuation pattern (whether with AsyncStream or withCheckedContinuation or whatever), that wrapped code will have to employ whatever pattern the legacy API requires.

So, in short, if an API wrapped by an AsyncStream requires a dispatch queue, then simply supply it a queue.

So:

extension NWPathMonitor {
    func paths() -> AsyncStream<NWPath> {
        AsyncStream { continuation in
            pathUpdateHandler = { path in
                continuation.yield(path)
            }
            continuation.onTermination = { [weak self] _ in
                self?.cancel()
            }
            start(queue: DispatchQueue(label: "NSPathMonitor.paths"))
        }
    }
}

Then you can do things like:

func startMonitoring() async {
    let monitor = NWPathMonitor()
    for await path in monitor.paths() {
        print(path.debugDescription)
    }
}

A few unrelated and stylistic recommendations, which I integrated in the above:

  1. I did not make this static, as we generally want our extensions to be as flexible as possible. If this is in an extension, we want the application developer to create whatever NWPathMonitor they want (e.g., perhaps requiring or prohibiting certain interfaces) and then create the asynchronous sequence for the updates for whatever path monitor they want.

  2. I made this a function, rather than a computed property, so that it is intuitive to an application developer that this will create a new sequence every time you call it. I would advise against hiding factories behind computed properties.

    The concern with a computed property is that it is not at all obvious to an application developer unfamiliar with the underlying implementation that if you access the same property twice that you will get two completely different objects. Using a method makes this a little more explicit.

Obviously, you are free to do whatever you want regarding these two observations, but I at least wanted to explain my rationale for the adjustments in the above code.