如何在 Swift 中使用 SCNetworkReachability [英] How to use SCNetworkReachability in Swift

查看:29
本文介绍了如何在 Swift 中使用 SCNetworkReachability的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试转换这个 Swift 的代码片段.由于一些困难,我正在努力下车.

I'm trying to convert this code snippet to Swift. I'm struggling on getting off the ground due to some difficulties.

- (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.

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.

var zeroAddress = sockaddr_in()

但我收到错误调用中参数'sin_len'缺少参数,这是可以理解的,因为该结构采用多个参数.所以我又试了一次.

But I get the error Missing argument for parameter 'sin_len' in call which is understandable because that struct takes a number of arguments. So I tried again.

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

正如预期的那样,我得到了一些其他错误在其自己的初始值内使用了变量.我也了解该错误的原因.在C中,他们先声明实例,然后填充参数.据我所知,这在 Swift 中是不可能的.所以我现在真的不知道该怎么做.

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.

我阅读了苹果官方的文档 关于在 Swift 中与 C API 交互,但它没有使用结构体的示例.

I read Apple's official document on interacting with C APIs in Swift but it has no examples in working with structs.

有人可以帮我吗?我真的很感激.

Can anyone please help me out here? I'd really appreciate it.

谢谢.

更新:多亏了 Martin,我才能够解决最初的问题.但是 Swift 仍然没有让我更容易.我收到多个新错误.

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.

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: 好的,我将此行更改为此,


EDIT 1: Okay I changed this line to this,

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

我在这一行遇到的新错误是'UnsafePointer' 不能转换为'CFAllocator'.如何在 Swift 中传递 NULL?

The new error I'm getting at this line is 'UnsafePointer' is not convertible to 'CFAllocator'. How to you pass NULL in Swift?

我也改变了这一行,现在错误消失了.

Also I changed this line and the error is gone now.

let didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags)

<小时>

编辑 2: 我在看到 这个 问题.但该答案与此处的答案相矛盾.它说 Swift 中没有与 NULL 等效的东西.


EDIT 2: I passed nil in this line after seeing this question. But that answer contradicts with the answer here. It says there is no equivalent to NULL in Swift.

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

无论如何,我收到一个新错误,上面说 'sockaddr_in' 与 'sockaddr' 不同.

Anyway I get a new error saying 'sockaddr_in' is not identical to 'sockaddr' at the above line.

推荐答案

(由于 Swift 语言的变化,这个答案被反复扩展,这让它有点混乱.我现在已经重写它并删除了所有指的是 Swift 1.x. 旧代码可以如果有人需要,可以在编辑历史记录中找到.)

这是您在 Swift 2.0 (Xcode 7) 中的做法:

This is how you would do it in 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 中有一个默认初始化器,它将结构体的所有字段初始化为零,因此可以使用

  • 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

var zeroAddress = sockaddr_in()

  • sizeofValue() 给出了这个结构的大小,它有为 sin_len 转换为 UInt8 :

  • sizeofValue() gives the size of this structure, this has to be converted to UInt8 for sin_len:

    zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress))
    

  • AF_INET 是一个 Int32,必须将其转换为 sin_family 的正确类型:

  • AF_INET is an Int32, this has to be converted to the correct type for sin_family:

    zeroAddress.sin_family = sa_family_t(AF_INET)
    

  • withUnsafePointer(&zeroAddress) { ... } 传递结构到闭包,用作参数SCNetworkReachabilityCreateWithAddress().UnsafePointer($0)需要转换,因为该函数需要一个指向sockaddr,而不是 sockaddr_in.

  • withUnsafePointer(&zeroAddress) { ... } passes the address of the structure to the closure where it is used as argument for SCNetworkReachabilityCreateWithAddress(). The UnsafePointer($0) conversion is needed because that function expects a pointer to sockaddr, not sockaddr_in.

    withUnsafePointer()返回的值为返回值来自 SCNetworkReachabilityCreateWithAddress() 并且具有输入 SCNetworkReachability?,即它是可选的.guard let 语句(Swift 2.0 中的一个新功能)将解包的值分配给 defaultRouteReachability 变量,如果它是不是 nil.否则执行 else 块并且函数返回.

    The value returned from withUnsafePointer() is the return value from SCNetworkReachabilityCreateWithAddress() and that has the type SCNetworkReachability?, i.e. it is an optional. The guard let statement (a new feature in Swift 2.0) assigns the unwrapped value to the defaultRouteReachability variable if it is not nil. Otherwise the else block is executed and the function returns.

    从 Swift 2 开始,SCNetworkReachabilityFlags 符合OptionSetType 具有类似集合的界面.你创建一个带有

    As of Swift 2, SCNetworkReachabilityFlags conforms to OptionSetType which has a set-like interface. You create an empty flags variable with

    var flags : SCNetworkReachabilityFlags = []
    

    并使用

    let isReachable = flags.contains(.Reachable)
    let needsConnection = flags.contains(.ConnectionRequired)
    

  • SCNetworkReachabilityGetFlags 的第二个参数的类型是UnsafeMutablePointer,这意味着你必须传递标志变量的地址.

  • The second parameter of SCNetworkReachabilityGetFlags has the type UnsafeMutablePointer<SCNetworkReachabilityFlags>, which means that you have to pass the address of the flags variable.

    还要注意,注册通知回调是可能的Swift 2,比较 使用来自 Swift 的 C APISwift 2 - UnsafeMutablePointer反对.

    Note also that registering a notifier callback is possible as of Swift 2, compare Working with C APIs from Swift and Swift 2 - UnsafeMutablePointer<Void> to object.

    Swift 3/4 更新:

    不安全的指针不能简单地转换为a的指针不同的类型了(见 - SE-0107 UnsafeRawPointer API).这里更新的代码:

    Unsafe pointers cannot be simply be converted to a pointer of a different type anymore (see - SE-0107 UnsafeRawPointer API). Here the updated code:

    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 中使用 SCNetworkReachability的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆