如何在Swift中使用SCNetworkReachability

我试图将这个代码片段转换为Swift。 由于一些困难,我正在艰难地起步。

- (BOOL) connectedToNetwork { // Create zero addy struct sockaddr_in zeroAddress; bzero(&zeroAddress, sizeof(zeroAddress)); zeroAddress.sin_len = sizeof(zeroAddress); zeroAddress.sin_family = AF_INET; // Recover reachability flags SCNetworkReachabilityRef defaultRouteReachability = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&zeroAddress); SCNetworkReachabilityFlags flags; BOOL didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags); CFRelease(defaultRouteReachability); if (!didRetrieveFlags) { return NO; } BOOL isReachable = flags & kSCNetworkFlagsReachable; BOOL needsConnection = flags & kSCNetworkFlagsConnectionRequired; return (isReachable && !needsConnection) ? YES : NO; } 

我遇到的第一个和主要的问题是如何定义和使用C结构。 在上面代码的第一行( struct sockaddr_in zeroAddress; )中,我想他们正在从struct sockaddr_in(?)定义一个名为zeroAddress的实例,我假设。 我试图像这样宣布一个var

 var zeroAddress = sockaddr_in() 

但是在调用中 ,我得到错误参数'sin_len'的错误参数,这是可以理解的,因为该结构需要一些参数。 所以我又试了一次。

 var zeroAddress = sockaddr_in(sin_len: sizeof(zeroAddress), sin_family: AF_INET, sin_port: nil, sin_addr: nil, sin_zero: nil) 

正如所料,我得到了一些其他的错误在自己的初始值使用的variables 。 我也了解这个错误的原因。 在C中,他们首先声明实例,然后填充参数。 就我所知,它在Swift中是不可能的。 所以我现在真的迷失在这个点上做什么。

我阅读了苹果在Swift中与C API进行交互的官方文档 ,但没有与结构一起工作的例子。

任何人都可以请帮我在这里? 我真的很感激。

谢谢。


更新:感谢马丁我能够通过最初的问题。 但斯威夫特仍然没有让我更容易。 我得到了多个新的错误。

 func connectedToNetwork() -> 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) var defaultRouteReachability: SCNetworkReachabilityRef = SCNetworkReachabilityCreateWithAddress(UnsafePointer<Void>, UnsafePointer<zeroAddress>) // 'zeroAddress' is not a type var flags = SCNetworkReachabilityFlags() let didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, UnsafeMutablePointer<flags>) // 'flags' is not a type defaultRouteReachability.dealloc(1) // 'SCNetworkReachabilityRef' does not have a member named 'dealloc' if didRetrieveFlags == false { return false } let isReachable: Bool = flags & kSCNetworkFlagsReachable // Cannot invoke '&' with an argument list of type '(@lvalue UInt32, Int)' let needsConnection: Bool = flags & kSCNetworkFlagsConnectionRequired // Cannot invoke '&' with an argument list of type '(@lvalue UInt32, Int)' return (isReachable && !needsConnection) ? true : false } 

编辑1:好吧,我改变了这一行,

 var defaultRouteReachability: SCNetworkReachabilityRef = SCNetworkReachabilityCreateWithAddress(UnsafePointer<Void>(), &zeroAddress) 

我在这一行得到的新错误是“UnsafePointer”不能转换为“CFAllocator” 。 如何在Swift中传递NULL

此外,我改变了这一行,现在错误消失了。

 let didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) 

编辑2:看到这个问题后,我通过了这一行的nil 。 但是这个答案与这里的答案相矛盾。 它说在Swift中没有相当于NULL

 var defaultRouteReachability: SCNetworkReachabilityRef = SCNetworkReachabilityCreateWithAddress(nil, &zeroAddress) 

无论如何,我收到一个新的错误,说'sockaddr_in'是不是'sockaddr'在上面的行相同。

(这个答案由于Swift语言的变化而不断被延伸,这使得它有点令人困惑,现在我已经重写了它,并删除了所有涉及到Swift 1.x的东西。如果有人需要,可以在编辑历史中find旧代码它。)

这是你如何做Swift 2.0(Xcode 7)

 import SystemConfiguration func connectedToNetwork() -> Bool { var zeroAddress = sockaddr_in() zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress)) zeroAddress.sin_family = sa_family_t(AF_INET) guard let defaultRouteReachability = withUnsafePointer(&zeroAddress, { SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($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) } 

说明:

  • 从Swift 1.2(Xcode 6.3)开始,导入的C结构体在Swift中有一个默认的初始化器,它初始化所有的结构字段为零,所以套接字地址结构可以初始化为

     var zeroAddress = sockaddr_in() 
  • sizeofValue()给出了这个结构的大小,这个必须被转换为用于sin_len

     zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress)) 
  • AF_INET是一个Int32 ,它必须被转换成sin_family的正确types:

     zeroAddress.sin_family = sa_family_t(AF_INET) 
  • withUnsafePointer(&zeroAddress) { ... }将结构的地址传递给用作SCNetworkReachabilityCreateWithAddress()参数的闭包。 UnsafePointer($0)转换是需要的,因为该函数需要一个指向sockaddr而不是sockaddr_in的指针。

  • withUnsafePointer()返回的值是来自withUnsafePointer()的返回值,它的types是SCNetworkReachability? ,即它是可选的。 guard let语句(Swift 2.0中的一个新特性)将unwrapped值赋给defaultRouteReachabilityvariables,如果它不nil 。 否则,执行else块并返回函数。

  • 从Swift 2开始, SCNetworkReachabilityCreateWithAddress()返回一个托pipe对象。 您不必明确地发布它。
  • 从Swift 2开始, SCNetworkReachabilityFlags符合具有类似接口的OptionSetType 。 你创build一个空的标志variables

     var flags : SCNetworkReachabilityFlags = [] 

    并检查标志

     let isReachable = flags.contains(.Reachable) let needsConnection = flags.contains(.ConnectionRequired) 
  • SCNetworkReachabilityGetFlags的第二个参数的types为UnsafeMutablePointer<SCNetworkReachabilityFlags> ,这意味着你必须传递flagsvariables的地址

还要注意,从Swift 2开始,注册一个通知器callback是可能的,比较使用Swift和Swift 2的 C API – UnsafeMutablePointer <Void>来处理对象 。


Swift 3(Xcode 8)的更新:

不安全的指针不能简单地转换为不同types的指针(请参阅 – SE-0107 UnsafeRawPointer API )。 这里更新的代码:

 import SystemConfiguration func connectedToNetwork() -> 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) } 

Swift 3,IPv4,IPv6

根据Martin R的回答:

 import SystemConfiguration func isConnectedToNetwork() -> Bool { guard let flags = getFlags() else { return false } let isReachable = flags.contains(.reachable) let needsConnection = flags.contains(.connectionRequired) return (isReachable && !needsConnection) } func getFlags() -> SCNetworkReachabilityFlags? { guard let reachability = ipv4Reachability() ?? ipv6Reachability() else { return nil } var flags = SCNetworkReachabilityFlags() if !SCNetworkReachabilityGetFlags(reachability, &flags) { return nil } return flags } func ipv6Reachability() -> SCNetworkReachability? { var zeroAddress = sockaddr_in6() zeroAddress.sin6_len = UInt8(MemoryLayout<sockaddr_in>.size) zeroAddress.sin6_family = sa_family_t(AF_INET6) return withUnsafePointer(to: &zeroAddress, { $0.withMemoryRebound(to: sockaddr.self, capacity: 1) { SCNetworkReachabilityCreateWithAddress(nil, $0) } }) } func ipv4Reachability() -> SCNetworkReachability? { var zeroAddress = sockaddr_in() zeroAddress.sin_len = UInt8(MemoryLayout<sockaddr_in>.size) zeroAddress.sin_family = sa_family_t(AF_INET) return withUnsafePointer(to: &zeroAddress, { $0.withMemoryRebound(to: sockaddr.self, capacity: 1) { SCNetworkReachabilityCreateWithAddress(nil, $0) } }) } 

这与Swift无关,但最好的解决scheme是不使用Reachability来确定networking是否在线。 只要build立连接并处理失败的错误。 build立连接有时可以激活hibernate的离线无线电。

可达性的一个有效用途是在networking从脱机状态转换到在线时使用它来通知您。 此时您应该重试失败的连接。

最好的解决scheme是使用Swift 2编写的ReachabilitySwift 类 ,并使用SCNetworkReachabilityRef

简单和容易:

 let reachability = Reachability.reachabilityForInternetConnection() reachability?.whenReachable = { reachability in // keep in mind this is called on a background thread // and if you are updating the UI it needs to happen // on the main thread, like this: dispatch_async(dispatch_get_main_queue()) { if reachability.isReachableViaWiFi() { print("Reachable via WiFi") } else { print("Reachable via Cellular") } } } reachability?.whenUnreachable = { reachability in // keep in mind this is called on a background thread // and if you are updating the UI it needs to happen // on the main thread, like this: dispatch_async(dispatch_get_main_queue()) { print("Not reachable") } } reachability?.startNotifier() 

像魅力一样工作。

请享用

更新了juanjo的答案来创build单例实例

 import Foundation import SystemConfiguration final class Reachability { private init () {} class var shared: Reachability { struct Static { static let instance: Reachability = Reachability() } return Static.instance } func isConnectedToNetwork() -> Bool { guard let flags = getFlags() else { return false } let isReachable = flags.contains(.reachable) let needsConnection = flags.contains(.connectionRequired) return (isReachable && !needsConnection) } private func getFlags() -> SCNetworkReachabilityFlags? { guard let reachability = ipv4Reachability() ?? ipv6Reachability() else { return nil } var flags = SCNetworkReachabilityFlags() if !SCNetworkReachabilityGetFlags(reachability, &flags) { return nil } return flags } private func ipv6Reachability() -> SCNetworkReachability? { var zeroAddress = sockaddr_in6() zeroAddress.sin6_len = UInt8(MemoryLayout<sockaddr_in>.size) zeroAddress.sin6_family = sa_family_t(AF_INET6) return withUnsafePointer(to: &zeroAddress, { $0.withMemoryRebound(to: sockaddr.self, capacity: 1) { SCNetworkReachabilityCreateWithAddress(nil, $0) } }) } private func ipv4Reachability() -> SCNetworkReachability? { var zeroAddress = sockaddr_in() zeroAddress.sin_len = UInt8(MemoryLayout<sockaddr_in>.size) zeroAddress.sin_family = sa_family_t(AF_INET) return withUnsafePointer(to: &zeroAddress, { $0.withMemoryRebound(to: sockaddr.self, capacity: 1) { SCNetworkReachabilityCreateWithAddress(nil, $0) } }) } } 

用法

 if Reachability.shared.isConnectedToNetwork(){ }