Using reachability library to Swiftui based app to notify when network is lost

Viewed 2015

I'm very much new to ios && using cocoapods.

I scanned over SO to find easiest way to detect network status and lots of answers directed me to Reachability git by AshleyMills.

I'm writing my webView app in swiftui & I want to pop up an alert/notifier when user's internet connection is lost. (So they don't sit idle while my webview tries to load)

It would be best if listner to network changes keeps running in the background while the app is on.

Most of the answers in SO seem to be Swift-based (appdelegate, ViewDidload, etc), which I don't know how to use because I started off with SwiftUI

Thanks in advance.

Edit(With attempts for Workaround that Lukas provided)

I tried the below. It compiles and runs BUT the alert wouldn't show up. Nor does it respond to connection change.

I have number of subViews in my ContentView. So I called timer && reachability on app level.

@State var currentDate = Date()
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
let reachability = try! Reachability()

@State private var showAlert: Bool = false

It seems to be not working:

WindowGroup {
        ContentView()
           .onAppear() {
               if !isConnected() {
                  self.showAlert = true
                  }
               }
           .onReceive(timer) { _ in
               if !isConnected() {
                  self.showAlert = true
           }else{
                  self.showAlert = false
           }
         }
         .alert(isPresented: $showAlert) {
             Alert(title: Text("Error"), message: Text("Your internet connection is too slow."), dismissButton: .default(Text("ok")))
          }
        }
2 Answers

NWPathMonitor was introduced in iOS 12 as a replacement for Reachability. A naive implementation would be as follows.

We could expose the status that comes back from pathUpdateHandler, however that would require you to import Network everywhere you wanted to use the status - not ideal. It would be better to create your own enum that maps to each of the cases provided by NWPath.Status, you can see the values here. I’ve created one that just handles the connected or unconnected states.

So we create a very simple ObservedObject that publishes our status. Note that as the monitor operates on its own queue and we may want to update things in the view we will need to make sure that we publish on the main queue.

import Network
import SwiftUI

// An enum to handle the network status
enum NetworkStatus: String {
    case connected
    case disconnected
}

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

    @Published var status: NetworkStatus = .connected

    init() {
        monitor.pathUpdateHandler = { [weak self] path in
            guard let self = self else { return }

            // Monitor runs on a background thread so we need to publish 
            // on the main thread
            DispatchQueue.main.async {
                if path.status == .satisfied {
                    print("We're connected!")
                    self.status = .connected

                } else {
                    print("No connection.")
                    self.status = .disconnected
                }
            }
        }
        monitor.start(queue: queue)
    }
}

Then we can use our Monitor class how we like, you could use it as a StateObject as I have done below, or you could use it as an EnvironmentObject. The choice is yours. Ideally you should have only a single instance of this class in your app.

struct ContentView: View {
    @StateObject var monitor = Monitor()
    // @EnvironmentObject var monitor: Monitor
    var body: some View {
        Text(monitor.status.rawValue)
    }
}

Tested in Playgrounds on an iPad Pro running iOS 14.2, and on Xcode 12.2 using iPhone X (real device) on iOS 14.3.

I worked one time with rechabiltiy and had the same problem. My solution is a work-around but in my case it was fine.

When the user get to the view where the connection should be constantly checked you can start a time(https://www.hackingwithswift.com/quick-start/swiftui/how-to-use-a-timer-with-swiftui). The timer calls a function in an interval of your choice where you can check the connection with the following code:

let reachability = try! Reachability()
 ...

func isConnected() -> Bool{
   if reachability.connection == .none{
       return "false" //no Connection
   }

   return true
}

}

You can also call this function .onAppear

Related