【问题标题】:Best Approach For Checking Internet Connection in iOS在 iOS 中检查 Internet 连接的最佳方法
【发布时间】:2016-08-02 16:39:29
【问题描述】:

我一直在互联网上搜索,以找到在 iOS 中检查互联网连接的最佳方法,该方法适用于 IPv4 和 IPv6 网络环境。我发现有很多可能的答案,但对应用哪一个非常困惑。我需要做的是:

   // Check for internet connection

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

我在互联网上找到了以下建议的选项。

选项 1: 使用苹果可达性类

借助 Reachability,我可以按以下方式检查 Internet 连接,这很短且直接,无需等待任何响应。

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

- (void)viewDidLoad {
    [super viewDidLoad];

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

}

但是这里的互联网检查是在飞行前完成的,这违反了苹果工程师的建议,如 WWDC video 所示。此外,如果您检查 ReadMe.md,就会提到 Reachability 完全支持 IPv6。但是如果在 Reachability.m 中检查了方法 reachabilityForInternetConnection,他们使用了 sockaddr_in,没有使用 sockaddr_in6,建议用于 IPv6 网络。所以我不知道它是否适用于IPv6网络。

选项 2:使用 Alamofire 连接

  • 只需尝试使用stackoverflow answer 之一进行连接。代码也如下所示:-

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

但是尝试连接到某个主机,等待响应然后知道是否在线/离线不是很耗时。

选项 3: 使用 Tony Million 的 Reachability 版本。但是作者警告说,在使用它时应用程序会被拒绝。我可以将其用作如下所示的代码:-

// 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];
}

但是在这个选项中,我的问题是,这是通过连接 http://google.com 来测试互联网的正确方法吗?这能保证 100% 的结果吗?如果谷歌宕机了怎么办?如果该应用程序在不允许使用 google.com 的国家/地区使用,结果如何?

所以我一直在选择哪个选项,如果这些选项是正确的方法,或者是否有更好的方法来检查互联网,它可以保证 100% 的结果并在 IPv4 和 IPv6 网络中工作,我一直处于两难的境地。谁能建议我哪个选项更好,或者我应该采用其他方法,实现此目的的更好方法是什么。

您的回复将不胜感激。

【问题讨论】:

  • 我会选择原生可达性。由于可能被拒绝,自动关闭选项 3。如果您已经在使用 Alamofire,选项 2 可能没问题。
  • 实际上,我会自己进行适当的 REST 调用,如果您确实收到没有 Internet 的错误,然后调用回退方法。使用随机域对其进行测试不会让您有任何收获。
  • 是的,这是推荐的方法。请参阅developer.apple.com/library/ios/documentation/… 中的“无需预检即可连接”。
  • 感谢您的回复。但是尝试连接 myDomain 并等待响应最终发现互联网不可用会不会很耗时。
  • 如果没有 Internet 连接,那么“连接​​”应该很快就会失败。你试过了吗?

标签: ios swift xcode reachability internet-connection


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    我认为您会发现此链接很有帮助: 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
    
        }
    }
    

    //用例

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

    这里有一些其他的研究链接:

    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

    【讨论】:

    • 我也曾想过使用这个解决方案。但这也适用于 IPv6,因为 sockaddr_in 根据本文档仅处理 IPv4 环境 [链接] developer.apple.com/library/ios/documentation/…
    • 我认为你想要做的是探索 SCNetworkReachabilityCreateWithName 方法而不是 SCNetworkReachabilityCreateWithAddress
    • @Asdrubal 这对 ipv4 和 ipv6 都有效吗?没有 ipv6 应用商店拒绝??
    【解决方案3】:

    阿拉莫火

    如果您已经将 Alamofire 用于所有 RESTful Api,那么您可以从中受益。

    您可以将以下类添加到您的应用中,并调用MNNetworkUtils.main.isConnected() 以获取其是否连接的布尔值。

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

    这是一个单例类。每次用户连接或断开网络时,它都会正确地将self.reachable 覆盖为真/假,因为我们在单例初始化时开始监听NetworkReachabilityManager

    另外,为了监控可达性,您需要提供一个主机,目前我使用的是google.com,如果需要,您可以随意更改为任何其他主机或您的主机。

    对于最佳方法,我认为如果您已经在使用 Alamofire,这可能是最佳方法。我不认为监控google.com 是一个糟糕的设计。如果我错了,请随时纠正我。

    【讨论】:

      猜你喜欢
      • 2011-01-03
      • 1970-01-01
      • 2013-01-08
      • 2017-12-07
      • 1970-01-01
      相关资源
      最近更新 更多