ios 如何在 Swift 中使用 SCNetworkReachability
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/25623272/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to use SCNetworkReachability in Swift
提问by Isuru
I'm trying to convert thiscode snippet to Swift. I'm struggling on getting off the ground due to some difficulties.
我正在尝试将此代码片段转换为 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;
}
The first and the main issue I'm having is on how to define and work with C structs. In the first line (struct sockaddr_in zeroAddress;
) of the above code, I think they're defining a instance called zeroAddress
from the struct sockaddr_in(?), I assume. I tried declaring a var
like this.
我遇到的第一个也是主要问题是如何定义和使用 C 结构。在struct sockaddr_in zeroAddress;
上面代码的第一行 ( ) 中,我认为他们定义了一个zeroAddress
从 struct sockaddr_in(?)调用的实例,我假设。我试着var
像这样声明一个。
var zeroAddress = sockaddr_in()
But I get the error Missing argument for parameter 'sin_len' in callwhich is understandable because that struct takes a number of arguments. So I tried again.
但是我在调用中收到了参数“sin_len”缺少参数的错误,这是可以理解的,因为该结构采用多个参数。所以我又试了一次。
var zeroAddress = sockaddr_in(sin_len: sizeof(zeroAddress), sin_family: AF_INET, sin_port: nil, sin_addr: nil, sin_zero: nil)
As expected I get some other error Variable used within its own initial value. I understand the cause of that error too. In C, they declare the instance first and then fill up the parameters. Its not possible in Swift as far as I know. So I'm truly lost at this point on what to do.
正如预期的那样,我在其自己的初始值中使用了一些其他错误变量。我也了解该错误的原因。在C中,他们先声明实例,然后填充参数。据我所知,这在 Swift 中是不可能的。所以在这一点上我真的不知道该怎么做。
I read Apple's official documenton interacting with C APIs in Swift but it has no examples in working with structs.
我阅读了 Apple关于在 Swift 中与 C API 交互的官方文档,但它没有使用结构的示例。
Can anyone please help me out here? I'd really appreciate it.
有人可以帮我吗?我真的很感激。
Thank you.
谢谢你。
UPDATE:Thanks to Martin I was able to get past the initial problem. But still Swift ain't making it easier for me. I'm getting multiple new errors.
更新:多亏了 Martin,我才能够解决最初的问题。但是 Swift 仍然没有让我更容易。我收到多个新错误。
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
}
EDIT 1:Okay I changed this line to this,
编辑 1:好的,我将此行更改为此,
var defaultRouteReachability: SCNetworkReachabilityRef = SCNetworkReachabilityCreateWithAddress(UnsafePointer<Void>(), &zeroAddress)
The new error I'm getting at this line is 'UnsafePointer' is not convertible to 'CFAllocator'. How to you pass NULL
in Swift?
我在这一行遇到的新错误是'UnsafePointer' is not convertible to 'CFAllocator'。你如何通过NULL
Swift?
Also I changed this line and the error is gone now.
我也改变了这一行,现在错误消失了。
let didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags)
EDIT 2:I passed nil
in this line after seeing thisquestion. But that answer contradicts with the answer here. It says there is no equivalent to NULL
in Swift.
编辑 2:我nil
在看到这个问题后通过了这一行。但该答案与此处的答案相矛盾。它说NULL
在 Swift 中没有等价物。
var defaultRouteReachability: SCNetworkReachabilityRef = SCNetworkReachabilityCreateWithAddress(nil, &zeroAddress)
Anyway I get a new error saying 'sockaddr_in' is not identical to 'sockaddr'at the above line.
无论如何,我收到一个新错误,说'sockaddr_in' 与上面一行中的'sockaddr' 不同。
回答by Martin R
(This answer was extended repeatedly due to changes in the Swift language, which made it a bit confusing. I have now rewritten it and removed everything which refers to Swift 1.x. The older code can be found in the edit history if somebody needs it.)
(由于 Swift 语言的变化,这个答案被反复扩展,这使它有点混乱。我现在已经重写它并删除了所有涉及 Swift 1.x 的内容。如果有人需要,可以在编辑历史记录中找到较旧的代码它。)
This is how you would do it in Swift 2.0 (Xcode 7):
这是您在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(var zeroAddress = sockaddr_in()
))
}) 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)
}
Explanations:
说明:
As of Swift 1.2 (Xcode 6.3), imported C structs have a default initializer in Swift, which initializes all of the struct's fields to zero, so the socket address structure can be initialized with
zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress))
sizeofValue()
gives the size of this structure, this has to be converted toUInt8
forsin_len
:zeroAddress.sin_family = sa_family_t(AF_INET)
AF_INET
is anInt32
, this has to be converted to the correct type forsin_family
:var flags : SCNetworkReachabilityFlags = []
withUnsafePointer(&zeroAddress) { ... }
passes the address of the structure to the closure where it is used as argument forSCNetworkReachabilityCreateWithAddress()
. TheUnsafePointer($0)
conversion is needed because that function expects a pointer tosockaddr
, notsockaddr_in
.The value returned from
withUnsafePointer()
is the return value fromSCNetworkReachabilityCreateWithAddress()
and that has the typeSCNetworkReachability?
, i.e. it is an optional. Theguard let
statement (a new feature in Swift 2.0) assigns the unwrapped value to thedefaultRouteReachability
variable if it is notnil
. Otherwise theelse
block is executed and the function returns.- As of Swift 2,
SCNetworkReachabilityCreateWithAddress()
returns a managed object. You don't have to release it explicitly. As of Swift 2,
SCNetworkReachabilityFlags
conforms toOptionSetType
which has a set-like interface. You create an empty flags variable withlet isReachable = flags.contains(.Reachable) let needsConnection = flags.contains(.ConnectionRequired)
and check for flags with
var zeroAddress = sockaddr_in()
The second parameter of
SCNetworkReachabilityGetFlags
has the typeUnsafeMutablePointer<SCNetworkReachabilityFlags>
, which means that you have to pass the addressof the flags variable.
从 Swift 1.2 (Xcode 6.3) 开始,导入的 C 结构体在 Swift 中有一个默认初始化器,它将结构体的所有字段初始化为零,因此可以使用
zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress))
sizeofValue()
给出此结构的大小,必须将其转换为UInt8
forsin_len
:zeroAddress.sin_family = sa_family_t(AF_INET)
AF_INET
是Int32
,这必须转换为正确的类型sin_family
:var flags : SCNetworkReachabilityFlags = []
withUnsafePointer(&zeroAddress) { ... }
将结构的地址传递给闭包,在那里它用作 的参数SCNetworkReachabilityCreateWithAddress()
。该UnsafePointer($0)
转换是必要的,因为该功能需要一个指针sockaddr
,而不是sockaddr_in
。from
withUnsafePointer()
返回的值是from的返回值SCNetworkReachabilityCreateWithAddress()
并且具有 typeSCNetworkReachability?
,即它是可选的。该guard let
语句(Swift 2.0 中的一个新功能)将解包的值分配给defaultRouteReachability
变量,如果它不是nil
。否则else
块被执行并且函数返回。- 从 Swift 2 开始,
SCNetworkReachabilityCreateWithAddress()
返回一个托管对象。您不必明确释放它。 从 Swift 2 开始,
SCNetworkReachabilityFlags
符合OptionSetType
其具有类似集合的接口。您创建一个空标志变量let isReachable = flags.contains(.Reachable) let needsConnection = flags.contains(.ConnectionRequired)
并检查标志
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, {
.withMemoryRebound(to: sockaddr.self, capacity: 1) { SCNetworkReachabilityCreateWithAddress(nil, ##代码##) } }) 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) }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, {
.withMemoryRebound(to: sockaddr.self, capacity: 1) { SCNetworkReachabilityCreateWithAddress(nil,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()
) } }) } 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, { ##代码##.withMemoryRebound(to: sockaddr.self, capacity: 1) { SCNetworkReachabilityCreateWithAddress(nil, ##代码##) } }) }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, {
.withMemoryRebound(to: sockaddr.self, capacity: 1) { SCNetworkReachabilityCreateWithAddress(nil,if Reachability.shared.isConnectedToNetwork(){ }
) } }) } 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, { ##代码##.withMemoryRebound(to: sockaddr.self, capacity: 1) { SCNetworkReachabilityCreateWithAddress(nil, ##代码##) } }) } }var window: UIWindow? var reachability = InternetReachability()! var reachabilityViewController : UIViewController? = nil func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. reachabilityChecking() return true } extension AppDelegate { func reachabilityChecking() { reachability.whenReachable = { reachability in DispatchQueue.main.async { print("Internet is OK!") if reachability.connection != .none && self.reachabilityViewController != nil { } } } reachability.whenUnreachable = { _ in DispatchQueue.main.async { print("Internet connection FAILED!") let storyboard = UIStoryboard(name: "Reachability", bundle: Bundle.main) self.reachabilityViewController = storyboard.instantiateViewController(withIdentifier: "ReachabilityViewController") let rootVC = self.window?.rootViewController rootVC?.present(self.reachabilityViewController!, animated: true, completion: nil) } } do { try reachability.startNotifier() } catch { print("Could not start notifier") } } }
的第二个参数是
SCNetworkReachabilityGetFlags
typeUnsafeMutablePointer<SCNetworkReachabilityFlags>
,这意味着你必须传递flags 变量的地址。
Note also that registering a notifier callback is possible as of Swift 2, compare Working with C APIs from Swiftand Swift 2 - UnsafeMutablePointer<Void> to object.
另请注意,从 Swift 2 开始可以注册通知程序回调,比较使用 Swift和Swift 2 中的C API - UnsafeMutablePointer<Void> 到 object。
Update for Swift 3/4:
Swift 3/4 更新:
Unsafe pointers cannot be simply be converted to a pointer of a different type anymore (see - SE-0107 UnsafeRawPointer API). Here the updated code:
不安全的指针不能再简单地转换为不同类型的指针(参见 - SE-0107 UnsafeRawPointer API)。这里更新的代码:
##代码##回答by juanjo
Swift 3, IPv4, IPv6
斯威夫特 3、IPv4、IPv6
Based on the Martin R's answer:
基于 Martin R 的回答:
##代码##回答by EricS
This has nothing to do with Swift, but the best solution is to NOT use Reachability to determine whether the network is online. Just make your connection and handle errors if it fails. Making a connection can at times fire up the dormant offline radios.
这与 Swift 无关,但最好的解决方案是不使用 Reachability 来确定网络是否在线。只需建立连接并在失败时处理错误。建立连接有时会启动休眠的离线无线电。
The one valid use of Reachability is to use it to notify you when a network transitions from offline to online. At that point you should retry failed connections.
Reachability 的一个有效用途是在网络从离线过渡到在线时使用它来通知您。此时您应该重试失败的连接。
回答by Bonnke
回答by anoop4real
updated juanjo's answer to create singleton instance
更新了 juanjo 的答案以创建单例实例
##代码##Usage
用法
##代码##回答by Sreekanth G
This is in Swift 4.0
这是在 Swift 4.0
I am using this framework https://github.com/ashleymills/Reachability.swift
And Install Pod ..
In AppDelegate
我正在使用这个框架https://github.com/ashleymills/Reachability.swift
并安装 Pod ..
在AppDelegate
The reachabilityViewController screen will appear if internet is not there
如果没有互联网,将会出现reachabilityViewController 屏幕