Best Approach For Checking Internet Connection in iOS
Asked Answered
N

3

12

I have been searching through internet to find the best way to check for internet connection in iOS that works for both in IPv4 and IPv6 network environment. I found there are many possible answers but very confused on which one to apply. What I need to do is the following:

   // Check for internet connection

if (internetConnectionIsAvailable) {
   // make rest calls
}else
   // show cached data
}

I found the following options that are suggested in internet.

OPTION 1: Use apple Reachability class

With Reachability I can check internet connectivity as following which is short and direct, and don't have to wait for any response.

 - (BOOL)connected
    {
        Reachability *reachability = [Reachability reachabilityForInternetConnection];
        NetworkStatus networkStatus = [reachability currentReachabilityStatus];
        return !(networkStatus == NotReachable);
    }

- (void)viewDidLoad {
    [super viewDidLoad];

    if (![self connected]){
     // Not connected
    }else{
    // Connected
    }

}

But here internet checking is done at pre-flight, which is against suggestion by apple engineer as seen on WWDC video. Also if you check ReadMe.md, it has been mentioned that Reachability fully supports IPv6. But if method reachabilityForInternetConnection is checked in Reachability.m they have used sockaddr_in, sockaddr_in6 has not been used which is recommended for IPv6 network. So I don't know if it will work in IPv6 network.

OPTION 2: Use Alamofire to connect

  • Just try to connect as in one of stackoverflow answer. The code also shown below:-

    Alamofire.request(.GET,"http://superrandomdomainnamethatisnotused.com/superrandompath").responseString {
        (request, response, stringData, error) in
    
        if let networkError = error {
            if (networkError.code == -1009) {
                println("No Internet \(error)")
            }
        }
    }
    

But isn't it time consuming to try to connect to some host, wait for the response and then know if online/offline.

OPTION 3: Use Tony Million's version of Reachability. But the author has warned of app being rejected while using this. I can use this as code shown below:-

// Checks if we have an internet connection or not
- (void)testInternetConnection
{   
    internetReachableFoo = [Reachability reachabilityWithHostname:@"www.google.com"];

    // Internet is reachable
    internetReachableFoo.reachableBlock = ^(Reachability*reach)
    {
        // Update the UI on the main thread
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"There in internet connection");
        });
    };

    // Internet is not reachable
    internetReachableFoo.unreachableBlock = ^(Reachability*reach)
    {
        // Update the UI on the main thread
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"Someone broke the internet :(");
        });
    };

    [internetReachableFoo startNotifier];
}

But in this option, my question is, is this right approach to test internet by connecting http://google.com . Does this guarantee 100% results? What if google is down? If the app is used in country where google.com is not allowed whats the result?

So I have been in a big dilemma which option to choose, if these options are the right way or is there a better way of checking internet which guarantee 100% results and works in both IPv4 and IPv6 network. Can anyone suggest me which option is better or should I go for other approach, what is the better way to accomplish this.

Your reply will be much appreciated.

Naji answered 2/8, 2016 at 16:39 Comment(6)
I would go with native Reachability. Automatically turn option 3 down because of possible rejection. Option 2 could be okay if you are already using Alamofire.Colatitude
Actually, I would just make the proper REST calls themselves, and if you do get an error for no Internet, then call the fallback method. Testing it with a random domain isn't going to get you anywhere.Colatitude
Yes, that is the recommend method. See "Connect Without Preflight" in developer.apple.com/library/ios/documentation/….Suisse
Thanks for the reply. But will that not be time consuming to try to connect myDomain and wait for the response to finally find out the internet is not available.Naji
If there is no Internet connection then a "connect" should fail quickly. Did you try it?Suisse
hmm, I haven't. But I will definitely try and mention the result.Naji
T
13

For Swift 3, Xcode 8......

func isInternetAvailable() -> Bool {

    var zeroAddress = sockaddr_in()
    zeroAddress.sin_len = UInt8(MemoryLayout<sockaddr_in>.size)
    zeroAddress.sin_family = sa_family_t(AF_INET)

    guard let defaultRouteReachability = withUnsafePointer(to: &zeroAddress, {
        $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
            SCNetworkReachabilityCreateWithAddress(nil, $0)
        }
    }) else {
        return false
    }

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

    let isReachable = flags.contains(.reachable)
    let needsConnection = flags.contains(.connectionRequired)

    return (isReachable && !needsConnection)
}
Tribulation answered 20/9, 2016 at 8:30 Comment(0)
A
7

I think you will find this link helpful: Network connection check crashing on IPv6 networks in Swift

import SystemConfiguration

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(sizeofValue(zeroAddress))
        zeroAddress.sin_family = sa_family_t(AF_INET)

        let defaultRouteReachability = withUnsafePointer(&zeroAddress) {
            SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, UnsafePointer($0))
        }

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

        let isReachable = flags == .Reachable
        let needsConnection = flags == .ConnectionRequired

        return isReachable && !needsConnection

    }
}

//use case

if Reachability.isConnectedToNetwork() {
    //do something
}

Here are a few other links for research:

https://developer.apple.com/reference/systemconfiguration/1514904-scnetworkreachabilitycreatewithn

https://developer.apple.com/library/prerelease/content/documentation/Networking/Conceptual/SystemConfigFrameworks/SC_Intro/SC_Intro.html#//apple_ref/doc/uid/TP40001065

Austria answered 2/8, 2016 at 16:53 Comment(3)
I had though of using this solution also. But will this work for IPv6 also, since sockaddr_in handles only IPv4 enviroment according to this document [link] developer.apple.com/library/ios/documentation/…Naji
I think what you want to do is explore the SCNetworkReachabilityCreateWithName method instead of SCNetworkReachabilityCreateWithAddressAustria
@Austria Is this work for both ipv4 & ipv6? without ipv6 app store rejection ??Affricate
H
1

Alamofire

If you are already using Alamofire for all the RESTful Api, here is what you can benifit from that.

You can add following class to your app, and call MNNetworkUtils.main.isConnected() to get a boolean on whether its connected or not.

#import Alamofire

class MNNetworkUtils {
  static let main = MNNetworkUtils()
  init() {
    manager = NetworkReachabilityManager(host: "google.com")
    listenForReachability()
  }

  private let manager: NetworkReachabilityManager?
  private var reachable: Bool = false
  private func listenForReachability() {
    self.manager?.listener = { [unowned self] status in
      switch status {
      case .notReachable:
        self.reachable = false
      case .reachable(_), .unknown:
        self.reachable = true
      }
    }
    self.manager?.startListening()
  }

  func isConnected() -> Bool {
    return reachable
  }
}

This is a singleton class. Every time, when user connect or disconnect the network, it will override self.reachable to true/false correctly, because we start listening for the NetworkReachabilityManager on singleton initialization.

Also in order to monitor reachability, you need to provide a host, currently I am using google.com feel free to change to any other hosts or one of yours if needed.

For best approach, I think if you are already using Alamofire this can be the best approach. I don't think monitoring google.com is a bad design. Feel free to correct me if I'm wrong.

Hanny answered 2/4, 2018 at 16:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.