GuiSoySauce
GuiSoySauce

Reputation: 1783

Network connection check crashing on IPv6 networks in Swift

I've recently got an app rejected for not being compatible with IPv6. The app was causing a crash when the following code was called. I suspect the crash is because it makes use of SCNetworkReachabilityCreateWithAddress when Apple recommends to not use that anymore.

Could anyone give me a hand and help making this code below compatible with both IPv6 and IPv4?

Code

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

        let defaultRouteReachability = withUnsafePointer(&zeroAddress) {
            SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0)).takeRetainedValue()
        }

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

        let isReachable = (flags & UInt32(kSCNetworkFlagsReachable)) != 0
        let needsConnection = (flags & UInt32(kSCNetworkFlagsConnectionRequired)) != 0

        return isReachable && !needsConnection
    }

}

Code source:

Check for internet connection availability in Swift

Code call on view did load:

if Reachability.isConnectedToNetwork() == false
{
      // internet is down
      let error = NSError(domain: "", code: 3, userInfo: nil)           let alertView = createDefaultAlertError(error.code)
      let tryAgainAction = UIAlertAction(title: ClassGeneralMessages().userMessageTryAgain, style: UIAlertActionStyle.Default) { (UIAlertAction) in                
}
else
{
     // internet is ok
     // run more code here
}

Upvotes: 1

Views: 3470

Answers (4)

MAhipal Singh
MAhipal Singh

Reputation: 4827

    func isConnectedToNetwork() -> Bool {
    
    var zeroAddress = sockaddr_in()
    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)
        }
    }
    //Commented code only work upto iOS Swift 2.3
    //    let defaultRouteReachability = withUnsafePointer(to: &zeroAddress) {
    //
    //        SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0))
    //    }
    
    var flags = SCNetworkReachabilityFlags()
    
    if !SCNetworkReachabilityGetFlags(defaultRouteReachability!, &flags) {
        return false
    }
    
    let isReachable = (flags.rawValue & UInt32(kSCNetworkFlagsReachable)) != 0
    let needsConnection = (flags.rawValue & UInt32(kSCNetworkFlagsConnectionRequired)) != 0
    return (isReachable && !needsConnection)
}

Upvotes: 0

Kavin Kumar Arumugam
Kavin Kumar Arumugam

Reputation: 1832

Try This:

class func isConnectedToNetwork() -> Bool
        {
            var zeroAddress = sockaddr_in6()
            zeroAddress.sin6_len = UInt8(MemoryLayout.size(ofValue: zeroAddress))
            zeroAddress.sin6_family = sa_family_t(AF_INET6)

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

            var flags = SCNetworkReachabilityFlags()
            if !SCNetworkReachabilityGetFlags(defaultRouteReachability!, &flags) {
                return false
            }
            let isReachable = (flags.rawValue & UInt32(kSCNetworkFlagsReachable)) != 0
            let needsConnection = (flags.rawValue & UInt32(kSCNetworkFlagsConnectionRequired)) != 0
            return (isReachable && !needsConnection)
        }

Upvotes: 1

bdash
bdash

Reputation: 18308

From Apple's documentation on supporting IPv6-only networks:

Connect Without Preflight

The Reachability APIs (see SCNetworkReachability Reference) are intended for diagnostic purposes after identifying a connectivity issue. Many apps incorrectly use these APIs to proactively check for an Internet connection by calling the SCNetworkReachabilityCreateWithAddress method and passing it an IPv4 address of 0.0.0.0, which indicates that there is a router on the network. However, the presence of a router doesn’t guarantee that an Internet connection exists. In general, avoid preflighting network reachability. Just try to make a connection and gracefully handle failures. If you must check for network availability, avoid calling the SCNetworkReachabilityCreateWithAddress method. Call the SCNetworkReachabilityCreateWithName method and pass it a hostname instead.

Your app appears to be doing both things it advises against:

  1. Using reachability APIs to perform a preflight, rather than only in event of a connection issue.
  2. Passing IPv4 literals to the reachability APIs rather than hostnames.

To address this I'd recommend not checking for a network connection up front, but to instead handle errors from your networking code.

Upvotes: 2

Christian Abella
Christian Abella

Reputation: 5797

You are getting a crash because you are showing an alert inside that code which should be run in the main UI thread. Put your UI code inside the dispatch_async going to the main UI thread and it should fix the problem.

dispatch_async(dispatch_get_main_queue(),
{
   let error = NSError(domain: "", code: 3, userInfo: nil)           let alertView = createDefaultAlertError(error.code)
      let tryAgainAction = UIAlertAction(title: ClassGeneralMessages().userMessageTryAgain, style: UIAlertActionStyle.Default) { (UIAlertAction) in }
})

Upvotes: 1

Related Questions