1

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
}
Community
  • 1
  • 1
GuiSoySauce
  • 1,763
  • 3
  • 24
  • 37

4 Answers4

2

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.

bdash
  • 18,110
  • 1
  • 59
  • 91
  • I've made a few updates to the question. The app got rejected again. I am following Apple's documentation. Also, not checking connection upfront. As you said, all networking errors are being handled by CloudKit operations. I am building for iOs9 with xCode 7.3.1. Completely lost in this one. – GuiSoySauce Jul 23 '16 at 03:31
  • You didn't just make a few minor updates, [you _completely_ changed the question](http://meta.stackexchange.com/questions/43478/exit-strategies-for-chameleon-questions)! – bdash Jul 26 '16 at 06:13
  • It is reverted back to the one that makes more sense to your answer @bdash – GuiSoySauce Jul 27 '16 at 06:47
1

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 }
})
Christian Abella
  • 5,747
  • 2
  • 30
  • 42
  • If there is an error when on IPv4 the alert view pops up with not crash. That makes me think its not UI related Seems the 'isConnectedToNetwork' is crashing when called to check an IPv6 network This is what I think Apple is calking about: Make sure you aren’t passing IPv4 address literals in dot notation to APIs such as getaddrinfo and SCNetworkReachabilityCreateWithName. Instead, use high-level network frameworks and address-agnostic versions of APIs, such as getaddrinfo and getnameinfo, and pass them hostnames or fully qualified domain names (FQDNs). See getaddrinfo(3) Mac OS X Dev.. – GuiSoySauce Jul 22 '16 at 02:34
  • If you (GulSoySauce) are going to quote directly from the Apple 'Guide and Sample Note' then you should put it in quotation marks and provide the full reference. Otherwise it is plagiarism!! All the text from "Make sure you aren't ...." to the end is from the Apple Documentation. Just saying. – PhilipS Oct 10 '16 at 21:40
1

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)
        }
Kavin Kumar Arumugam
  • 1,792
  • 3
  • 28
  • 47
0
    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)
}
Jarvis The Avenger
  • 2,750
  • 1
  • 19
  • 37
MAhipal Singh
  • 4,745
  • 1
  • 42
  • 57