Reachability.swift icon indicating copy to clipboard operation
Reachability.swift copied to clipboard

Reachability(hostname: host) does not work

Open msedd opened this issue 8 years ago • 22 comments

My IPad is connected to an Wifi hotspot without any connection to the internet. If I try this code, I receive true.

let host = "www.google.com"
let reachability: Reachability
do {
    reachability = try Reachability(hostname: host)
    nwAvailable = reachability.isReachableViaWiFi()
} catch {
    ...
}

If the hotspot has internet or the wifi is disconnected, the code works very well.

msedd avatar Jun 20 '16 21:06 msedd

I have same issue:

func initReachabilityForServer() {
        do {
            self.reachabilityForServer = try Reachability(hostname: "myserver.com")
        } catch ReachabilityError.FailedToCreateWithAddress(let address) {
            print("Unable to create\nReachability with address:\n\(address)")
            return
        } catch {}

        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.reachabilityForServerChanged),name: ReachabilityChangedNotification,object: self.reachabilityForServer)

        do {
            try self.reachabilityForServer?.startNotifier()
        } catch {
            print("can not start notify for server")
        }
    }

anhhtz avatar Jun 21 '16 01:06 anhhtz

Same issue here, it returns true even if there's no internet connection as long as the device is connected to wifi or cellular.

yirj avatar Jun 29 '16 02:06 yirj

Yep, having the same issue. It always seems to register an active WiFi or cellular connection as reachable, regardless of whether or not the hostname is.

conmulligan avatar Jul 22 '16 15:07 conmulligan

Have you run the iOS sample app? When the hostname is set to "invalidhost" it shows as Not Reachable

ashleymills avatar Aug 05 '16 16:08 ashleymills

yes its work, but this is not the case

please remove the internet connection (e.g. DSL cable) from your router and test this code:

let dispatchTime = dispatch_time(DISPATCH_TIME_NOW, Int64(UInt64(5) * NSEC_PER_SEC))
        dispatch_after(dispatchTime, dispatch_get_main_queue()) {
            self.stopNotifier()
            self.setupReachability(hostName: "google.com", useClosures: true)
            self.startNotifier()

            /*
            let dispatchTime = dispatch_time(DISPATCH_TIME_NOW, Int64(UInt64(5) * NSEC_PER_SEC))
            dispatch_after(dispatchTime, dispatch_get_main_queue()) {
                self.stopNotifier()
                self.setupReachability(hostName: "invalidhost", useClosures: true)
                self.startNotifier()            }
*/

It shows always:

--- set up with host name: google.com --- start notifier -R ------- - WiFi

msedd avatar Aug 05 '16 21:08 msedd

how to monitor internet checking in this case:- I have connected to WIFI but ,i turned off my internet connection,in this case notify method is not calling

DivakarIOS avatar Dec 08 '16 18:12 DivakarIOS

Any update here?

JAManfredi avatar Apr 08 '17 17:04 JAManfredi

Hi @ashleymills ! Have you any thoughts how to solve this bug? This is only the reason why I don't use it.

achirkof avatar May 03 '17 10:05 achirkof

@achirkof I haven't, sorry - but I'm happy to take a pull request if anyone else has a fix.

Does anyone know if this is also an issue with Apple's objective-C Reachability class? Can I suggest you also ask this question on Stack Overflow

ashleymills avatar May 03 '17 10:05 ashleymills

no luck?

farzadshbfn avatar May 05 '17 07:05 farzadshbfn

I have decided to not use reachability, this or apple it doesn't matter. My solution is to send a request always and handle error then. I can show error message for user in my UI if needed. If it is post request, I can mark it as not synchronized and make delayed sync after. I use Alamofire to work with REST Api.

achirkof avatar May 05 '17 07:05 achirkof

@achirkof Thanks. The thing is, I'm always showing an error or something to the user with a retry button so he/she'd be able to trigger the Api call again... but it would be nice to do it before hand. Which comes very handy using RxSwift but host reachability is really a problem here.

farzadshbfn avatar May 05 '17 08:05 farzadshbfn

Still have this issue. For example, my host is 192.168.0.104 (local ip in my Mac). But if I still get availability status if I turn off the Mac in my iPhone application.

zhouhao27 avatar Sep 04 '17 01:09 zhouhao27

I'm experiencing this bug still on release 4.2.1

HenryGlendening avatar Sep 30 '18 21:09 HenryGlendening

Bump. Still having this issue on v4.3. Would love to use Reachability in production if this issue could be resolved!

davidcorbin avatar Nov 21 '18 15:11 davidcorbin

Just inherited a code base using Reachability and this issue is present. It's an edge case but one that I think should be addressed.

michealbeatty avatar Nov 26 '18 20:11 michealbeatty

I don't think you can fix it while using the SCNetworkReachability interface. From the SCNetworkReachability Apple docs:

The SCNetworkReachability programming interface allows an application to determine the status of a system's current network configuration and the reachability of a target host. A remote host is considered reachable when a data packet, sent by an application into the network stack, can leave the local device. Reachability does not guarantee that the data packet will actually be received by the host.

You'd have to be able to send something to the remote host and get a response to cover this edge case. Something like a ping or small network request. That's not a perfect solution either as some router configurations block incoming pings.

opi-smccoole avatar Dec 14 '18 20:12 opi-smccoole

Is there no change? I would like to know if the wifi network can actually ping any outside ips/hosts

hilld1 avatar Jan 13 '19 12:01 hilld1

Please help with this solution @ashleymills ASAP.

maulik6994 avatar Jul 23 '19 13:07 maulik6994

Up!

chiliec avatar Aug 06 '19 09:08 chiliec

It works for me, but a little not as expected.

I turned off internet connection in router and run this code:

let reachability = Reachability()!

var isInternetAvailable: Bool = true {
    didSet {
        print("Intenet is \(isInternetAvailable ? "available" : "not available")")
    }
}

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    reachability.whenReachable = { reachability in
        self.isInternetAvailable = true
    }
    reachability.whenUnreachable = { _ in
        self.isInternetAvailable = false
    }
    try! reachability.startNotifier()
    return true
}

in log messages I see only:

Intenet is available

But after added hostname:

let reachability = Reachability(hostname: "google.com")!

log messages was changed:

Intenet is available
Intenet is not available

and isInternetAvailable variable is equal to false as expected!

chiliec avatar Aug 06 '19 11:08 chiliec

import Foundation
import Reachability

enum ReachabilityChange {
    case available
    case unavailable
}

protocol NetworkReachable: AnyObject {
    func startReachability()
    var reachabilityChangeHandler: ((ReachabilityChange) -> Void)? { get set }
    var reachability: Reachability? { get set }
}

extension NetworkReachable {
    func emitReachability(_ change: ReachabilityChange) {
        reachabilityChangeHandler?(change)
    }
    
    func startReachability() {
        startHost()
    }
    
    func startHost() {
        stopNotifier()
        setupReachability(website_url)
        startNotifier()
        DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
            self.startHost()
        }
    }
    
    func setupReachability(_ hostName: String) {
        var reachability: Reachability?
        reachability = try? Reachability(hostname: hostName)
        self.reachability = reachability
        
        if reachability?.connection == .unavailable {
            self.emitReachability(.unavailable)
        } else {
            self.verifyURL(urlPath: hostName, completion: { (isUrlAccessible) in
                if isUrlAccessible {
                    self.emitReachability(.available)
                } else {
                    self.emitReachability(.unavailable)
                }
            })
        }
    }
    
    func startNotifier() {
        do {
            try reachability?.startNotifier()
        } catch {
            return
        }
    }
    
    func stopNotifier() {
        reachability?.stopNotifier()
        reachability = nil
    }
    
    func verifyURL(urlPath: String, completion: @escaping (_ isUrlAccessible: Bool) -> Void) {
        if let url = URL(string: urlPath) {
            var request = URLRequest(url: url)
            request.timeoutInterval = 5
            let config = URLSessionConfiguration.default
            config.requestCachePolicy = .reloadIgnoringLocalCacheData
            config.urlCache = nil
            let task = URLSession(configuration: config).dataTask(with: request) { _, response, _ in
                if let httpResponse = response as? HTTPURLResponse {
                    if httpResponse.statusCode == 200 {
                        completion(true)
                    } else {
                        completion(false)
                    }
                } else {
                    completion(false)
                }
            }
            task.resume()
        } else {
            completion(false)
        }
    }
}

this is (NetworkReachable) a protocol extension over the reachability

you can use it on every where or every viewmodel you want.

such as:

import Foundation
import Reachability

final class SceneDelegateViewModel: NetworkReachable {
    var reachability: Reachability?
    var reachabilityChangeHandler: ((ReachabilityChange) -> Void)?
}

for example I use it on scene delegate:

   var viewModel = SceneDelegateViewModel()
   func scene(_ scene: UIScene,
               willConnectTo session: UISceneSession,
               options connectionOptions: UIScene.ConnectionOptions) {
        guard let scene = (scene as? UIWindowScene) else { return }
        self.window = UIWindow(windowScene: scene)
        bindViewModel()
        //set rootviewcontroller
        viewModel.startReachability()
    }
    
    func bindViewModel() {
        viewModel.reachabilityChangeHandler = { [weak self] change in
            guard let strongSelf = self else { return }
            switch change {
            case .available:
                DispatchQueue.main.async {
                    strongSelf.noInternetVC.dismiss(animated: true)
                }
            case .unavailable:
                DispatchQueue.main.async {
                    if strongSelf.noInternetVC.presentingViewController == nil {
                        strongSelf.noInternetVC.modalPresentationStyle = .fullScreen
                        strongSelf.window?.rootViewController?.present(strongSelf.noInternetVC, animated: true)
                    }
                }
            }
        }
    }

every where you used NetworkReachable protocol you must call startReachability() and bind it that's it.

maziar avatar Feb 09 '23 15:02 maziar