Vahid
Vahid

Reputation: 1939

using NWPathMonitor with BehaviorSubject to monitor network connectivity

I need an observer to keep my app updated on the connectivity status of the device.

NWPathMonitor seems to be the standard approach. So I go like this:

class NetworkService {
   let monitor = NWPathMonitor()
   let connected = BehaviorSubject(value: true)
   private init() {
        monitor.pathUpdateHandler = { path in
           let value = path.status == .satisfied
           self.connected.onNext(value)
        }
        let queue = DispatchQueue(label: "NetworkMonitor")
        monitor.start(queue: queue)
   }
}

And this is where I subscribe to connected

NetworkService.shared.connected.subscribe(onNext: { connected in
   print("network connected: \(connected)")
}).disposed(by: disposeBag)

As soon as the app starts, onNext starts firing like crazy, flooding the console with network connected: true until the app crashes.

I tried adding a local cache variable so the onNext part fires only if there's been a change on the value.

if (value != self.previousValue) {
   self.previousValue = value
   self.connected.onNext(value)
}

Same happens still. So I guessed maybe the monitor is updating too frequently to allow the cache variable to get assigned, and I tried adding a semaphore ...

self.semaphore.wait()
if (value != self.previousValue) {
    self.previousValue = value
    self.connected.onNext(value)
}
self.semaphore.signal()

And event that didn't help. Still getting a flood of print messages and the app crashes.

BTW, if you were wondering this is how I declare the semaphore in my class:

let semaphore = DispatchSemaphore( value: 1)

Upvotes: 2

Views: 1145

Answers (1)

Daniel T.
Daniel T.

Reputation: 33967

I'm not seeing the same behavior from the class as you, but a simple solution is to use .distinctUntilChanged() which will stop an event from propagating unless it is different than the previous event.

If the above doesn't stop the flood of events, then the problem isn't with the code you have presented, but with something else you haven't told us about.

Also, I would have written it like this:

extension NWPathMonitor {
    var rx_path: Observable<NWPath> {
        Observable.create { [self] observer in
            self.pathUpdateHandler = { path in
                observer.onNext(path)
            }
            let queue = DispatchQueue(label: "NetworkMonitor")
            self.start(queue: queue)
            return Disposables.create {
                self.cancel()
            }
        }
    }
}

With the above, it's easy to access by doing:

let disposable = NWPathMonitor().rx_path
    .map { $0.status == .satisfied }
    .debug()
    .subscribe()

The subscription will keep the NWPathMonitor object alive for the duration of the subscription. Calling dispose() on the disposable will shut down the subscription and release the NWPathMonitor object.

Upvotes: 1

Related Questions