1

I have a Network Monitor from where I want to receive notifications when status has changed.
Looks like this:

 final class NetworkMonitor: ObservableObject {
    let monitor = NWPathMonitor()
    let queue = DispatchQueue(label: "Monitor")

    static let shared = NetworkMonitor()

    @Published var status: NetworkStatus = .connected

    func start() {
        self.monitor.pathUpdateHandler = { [weak self] path in
            guard let self = self else { return }
            DispatchQueue.main.async {
                self.status = (path.status == .satisfied) ? .connected : .disconnected
            }
        }

        self.monitor.start(queue: self.queue)
    }
}

I create in Home a @StateObject of Network monitor and send it by an environment object.

struct HomeView: View {
    @StateObject var networkMonitor = NetworkMonitor()

    var body: some View {
        NavigationView {
            ContentView().environmentObject(networkMonitor)
        }
     }
 }

And I want in ContentView to receive any changes occur.

struct ContentView: View {
  @EnvironmentObject var networkMonitor: NetworkMonitor
  var body: some View {
     VStack {
         Text("Example")
     }.onReceive(self.networkMonitor.$status, perform: { status in
            print("onReceive \(status)") // <---- this doesn't trigger
       })
   }
}

I can't figure it out why whenever a change occurs in network status, the onReceive doesn't trigger.

EDIT: I am using shared instance to start monitoring in AppDelegate.

     func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        NetworkMonitor.shared.start()
}
3
  • Where are you calling start? Why is there a shared property (making it look like a singleton), but then you're creating an instance and passing it via envrionmentObject? Are you sure you aren't using shared somewhere else and using a different instance than what you have in your view? Commented Jun 22, 2021 at 19:29
  • @jnpdx I added an edit for your response. Commented Jun 22, 2021 at 19:32
  • Looks like what I suspected in the second half of my comment -- you're using a different instance in your AppDelegate than you are in your view. Commented Jun 22, 2021 at 19:33

1 Answer 1

5

You're using a different instance in your AppDelegate (shared) than you are in your HomeView (NetworkMonitor()).

Usually, when you use a singleton pattern, you want to make the init private so that you can avoid this mistake:

final class NetworkMonitor: ObservableObject {
    let monitor = NWPathMonitor()
    let queue = DispatchQueue(label: "Monitor")

    static let shared = NetworkMonitor()

    @Published var status: NetworkStatus = .connected

    private init() { } //<-- Here

    func start() {
        self.monitor.pathUpdateHandler = { [weak self] path in
            guard let self = self else { return }
            DispatchQueue.main.async {
                self.status = (path.status == .satisfied) ? .connected : .disconnected
            }
        }

        self.monitor.start(queue: self.queue)
    }
}
struct HomeView: View {
    @StateObject var networkMonitor = NetworkMonitor.shared //<-- Here

    var body: some View {
        NavigationView {
            ContentView().environmentObject(networkMonitor)
        }
     }
 }
Sign up to request clarification or add additional context in comments.

1 Comment

omg I'm so stupid 🤦‍♂️ thank you, you have brightened my day. I'll make this the accepted answear

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.