Reachability.swift
Reachability.swift copied to clipboard
Reachability(hostname: host) does not work
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.
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")
}
}
Same issue here, it returns true even if there's no internet connection as long as the device is connected to wifi or cellular.
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.
Have you run the iOS sample app? When the hostname is set to "invalidhost" it shows as Not Reachable
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
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
Any update here?
Hi @ashleymills ! Have you any thoughts how to solve this bug? This is only the reason why I don't use it.
@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
no luck?
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 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.
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.
I'm experiencing this bug still on release 4.2.1
Bump. Still having this issue on v4.3. Would love to use Reachability in production if this issue could be resolved!
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.
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.
Is there no change? I would like to know if the wifi network can actually ping any outside ips/hosts
Please help with this solution @ashleymills ASAP.
Up!
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!
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.