2

I am working on a complex app and I want to test both host in internet reachability on each ViewController that receive data from server I am currently using this library for Reachability

https://github.com/ashleymills/Reachability.swift

And I want to create an easy method or extension that check the reachability for both internet and host out of this I have already use the sample code from the library which is below: `import UIKit

import Reachability

class VC22: UIViewController {

@IBOutlet weak var networkStatus: UILabel!
@IBOutlet weak var hostNameLabel: UILabel!

var reachability: Reachability?

override func viewDidLoad() {
    super.viewDidLoad()

    // Start reachability without a hostname intially
    setupReachability(nil, useClosures: true)
    startNotifier()

    // After 5 seconds, stop and re-start reachability, this time using a hostname
    let dispatchTime = DispatchTime.now() + DispatchTimeInterval.seconds(5)
    DispatchQueue.main.asyncAfter(deadline: dispatchTime) {
        self.stopNotifier()
        self.setupReachability("http://81.28.42.42:4242/", useClosures: true)
        self.startNotifier()

        let dispatchTime = DispatchTime.now() + DispatchTimeInterval.seconds(5)
        DispatchQueue.main.asyncAfter(deadline: dispatchTime) {
            self.stopNotifier()
            self.setupReachability("invalidhost", useClosures: true)
            self.startNotifier()            }

    }
}

func setupReachability(_ hostName: String?, useClosures: Bool) {
    hostNameLabel.text = hostName != nil ? hostName : "No host name"

    print("--- set up with host name: \(hostNameLabel.text!)")

    let reachability = hostName == nil ? Reachability() : Reachability(hostname: hostName!)
    self.reachability = reachability

    if useClosures {
        reachability?.whenReachable = { reachability in
            DispatchQueue.main.async {
                self.updateLabelColourWhenReachable(reachability)
            }
        }
        reachability?.whenUnreachable = { reachability in
            DispatchQueue.main.async {
                self.updateLabelColourWhenNotReachable(reachability)
            }
        }
    } else {
        NotificationCenter.default.addObserver(self, selector: #selector(VC22.reachabilityChanged(_:)), name: ReachabilityChangedNotification, object: reachability)
    }
}

func startNotifier() {
    print("--- start notifier")
    do {
        try reachability?.startNotifier()
    } catch {
        networkStatus.textColor = .red
        networkStatus.text = "Unable to start\nnotifier"
        return
    }
}

func stopNotifier() {
    print("--- stop notifier")
    reachability?.stopNotifier()
    NotificationCenter.default.removeObserver(self, name: ReachabilityChangedNotification, object: nil)
    reachability = nil
}

func updateLabelColourWhenReachable(_ reachability: Reachability) {
    print("\(reachability.description) - \(reachability.currentReachabilityString)")
    if reachability.isReachableViaWiFi {
        self.networkStatus.textColor = .green
    } else {
        self.networkStatus.textColor = .blue
    }

    self.networkStatus.text = reachability.currentReachabilityString
}

func updateLabelColourWhenNotReachable(_ reachability: Reachability) {
    print("\(reachability.description) - \(reachability.currentReachabilityString)")

    self.networkStatus.textColor = .red

    self.networkStatus.text = reachability.currentReachabilityString
}


func reachabilityChanged(_ note: Notification) {
    let reachability = note.object as! Reachability

    if reachability.isReachable {
        updateLabelColourWhenReachable(reachability)
    } else {
        updateLabelColourWhenNotReachable(reachability)
    }
}

deinit {
    stopNotifier()
}

}

this works fine but I just need a boolean to tell me if it is connected or not that could be reusable through the app

UPDATE currently I am using below class:

import Foundation
import SystemConfiguration

public class Reachability {

    class func isConnectedToNetwork() -> Bool {

        var zeroAddress = sockaddr_in(sin_len: 0, sin_family: 0, sin_port: 0, sin_addr: in_addr(s_addr: 0), sin_zero: (0, 0, 0, 0, 0, 0, 0, 0))
        zeroAddress.sin_len = UInt8(MemoryLayout.size(ofValue: zeroAddress))
        zeroAddress.sin_family = sa_family_t(AF_INET)

        let defaultRouteReachability = withUnsafePointer(to: &zeroAddress) {
            $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {zeroSockAddress in
                SCNetworkReachabilityCreateWithAddress(nil, zeroSockAddress)
            }
        }

        var flags: SCNetworkReachabilityFlags = SCNetworkReachabilityFlags(rawValue: 0)
        if SCNetworkReachabilityGetFlags(defaultRouteReachability!, &flags) == false {
            return false
        }

        let isReachable = flags == .reachable
        let needsConnection = flags == .connectionRequired

        return isReachable && !needsConnection

    }
}

and use in viewControllers like below :

 if Reachability.isConnectedToNetwork() == true {
        print("Internet connection OK")

      JSONParseFunction()

} else {
    print("Internet connection FAILED")
    let alert = UIAlertView(title: "You are not connect to internet", message: "please check you connectivity", delegate: nil, cancelButtonTitle: "OK")
    alert.show()
}

This way I just check for internet I need to check both Host and Internet

Ashley Mills
  • 50,474
  • 16
  • 129
  • 160
UncleJunior
  • 231
  • 1
  • 2
  • 12

1 Answers1

2

Is there a reason for not just dropping this in your AppDelegate and subscribe to the observer there instead of doing it on a specific vc?

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        prepareReachabilityObserver()
        return true
    }

    private func prepareReachabilityObserver() {
        AppDelegate.reachability.whenUnreachable = { reachability in
            DispatchQueue.main.async {
                print("Not reachable")
            }
        }

        do {
            try AppDelegate.reachability.startNotifier()
        } catch {
            print("Unable to start notifier")
        }
    }

On the host verification extension part, I'd probably go with something like:

extension UIViewController {
    internal func isReachable() -> Bool {
            //this is the function you already have.
    }
}

which you can then use in every viewcontroller by doing

self.isReachable() //'self' is a UIViewController in this case.

Other than this, I'm having trouble understanding your question, since you seem to already have this solved.

EDIT: I think I understand your question, now. You want to check both if you're reachable and if the hostname you pass around is also reachable. I don't think it's the best idea to handle both at the same time, since one is a Reachability issue ('can I get an outgoing connection?') and the other one is a connection issue ('can I get a response from this remote place?' or 'Does this request timeout?').

The way I currently handle it is Reachability in something like the AppDelegate and then handling the timeout on a request-by-request manner (which you can then generalize within the network-stuff scope). To be more specific: AppDelegate sets Reachability. Then I have a RequestsManager that handles service calls with a configured timeout.

Then you can do something like:

RequestManager.makeRequest("https://an.endpoint.of.yours",
        onSuccess: {},
        onFailure: { //Here goes your timeout behaviour.
})

Where do I pass the hostname? I think that's an unneeded behaviour, to be honest. You don't try to open door and then you open door. You just try and see if you're successful. Here's the same. You try to make a request, does it succeed? Awesome. Does it not? Handle accordingly. What do you (The app) care if the failure was because the endpoint was down or because you don't have a working data plan or network's down? The request times out either way, and that's what you care about.

Again, this is all assuming I actually understood you, which I'm not 100% sure.

Ale Ravasio
  • 98
  • 12
  • You should import Reachability in your AppDelegate file. `import Reachability` like you already did in your example. Also, I heavily updated the answer because I wasn't sure I fully understood the question. Please confirm. – Ale Ravasio May 02 '17 at 20:21
  • Thanks for in-depth answer – UncleJunior May 02 '17 at 20:36