Network connection check crashing on IPv6 networks in Swift

耗尽温柔 提交于 2019-12-04 05:24:52

问题


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
}

回答1:


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.




回答2:


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 }
})



回答3:


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)
        }



回答4:


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)

}



来源:https://stackoverflow.com/questions/38516953/network-connection-check-crashing-on-ipv6-networks-in-swift

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!