如何在Swift中使用SCNetworkReachability

问题描述 投票:97回答:6

我正在尝试将this代码段转换为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)

正如预期的那样,我在其自己的初始值中使用了一些其他错误变量。我也理解这个错误的原因。在C中,它们首先声明实例,然后填充参数。据我所知,它在Swift中是不可能的。所以我真的迷失了这一点。

我在Swift中阅读了Apple的官方document与C API的交互,但它没有使用结构的例子。

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

谢谢。


更新:感谢Martin,我能够解决最初的问题。但斯威夫特仍然没有让我更容易。我收到了多个新错误。

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'。你怎么在斯威夫特传递NULL

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

let didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags)

编辑2:在看到nil问题后,我在这条线上通过了this。但这个答案与答案here相矛盾。它说在斯威夫特没有相当于NULL

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

无论如何,我得到一个新的错误,说'sockaddr_in'与上面一行的'sockaddr'不同。

ios c struct swift reachability
6个回答
232
投票

(由于Swift语言的变化,这个答案反复扩展,这使得它有点令人困惑。我现在已经重写了它并删除了所有引用Swift 1.x的内容。如果有人需要,可以在编辑历史中找到旧代码它。)

这就是你在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中有一个默认的初始化器,它将所有struct的字段初始化为零,因此套接字地址结构可以初始化为 var zeroAddress = sockaddr_in()
  • sizeofValue()给出了这个结构的大小,这必须转换为UInt8sin_lenzeroAddress.sin_len = UInt8(sizeofValue(zeroAddress))
  • AF_INET是一个Int32,这必须转换为sin_family的正确类型: zeroAddress.sin_family = sa_family_t(AF_INET)
  • withUnsafePointer(&zeroAddress) { ... }将结构的地址传递给闭包,在那里它被用作SCNetworkReachabilityCreateWithAddress()的参数。需要进行UnsafePointer($0)转换,因为该函数需要指向sockaddr的指针,而不是sockaddr_in
  • withUnsafePointer()返回的值是来自SCNetworkReachabilityCreateWithAddress()的返回值,其类型为SCNetworkReachability?,即它是可选的。 guard let语句(Swift 2.0中的一个新特性)将未包装的值分配给defaultRouteReachability变量(如果它不是nil)。否则执行else块并返回该函数。
  • 从Swift 2开始,SCNetworkReachabilityCreateWithAddress()返回一个托管对象。您不必明确释放它。
  • 从Swift 2开始,SCNetworkReachabilityFlags符合OptionSetType,它具有类似集合的界面。您使用创建一个空标志变量 var flags : SCNetworkReachabilityFlags = [] 并检查标志 let isReachable = flags.contains(.Reachable) let needsConnection = flags.contains(.ConnectionRequired)
  • SCNetworkReachabilityGetFlags的第二个参数的类型为UnsafeMutablePointer<SCNetworkReachabilityFlags>,这意味着你必须传递flags变量的地址。

另请注意,从Swift 2开始,可以注册通知程序回调,比较Working with C APIs from SwiftSwift 2 - UnsafeMutablePointer<Void> to object


Swift 3/4更新:

不安全的指针不能简单地转换为不同类型的指针(参见 - 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)
}

11
投票

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

6
投票

这与Swift无关,但最好的解决方案是不使用Reachability来确定网络是否在线。如果失败,只需建立连接并处理错误。建立连接有时可以启动休眠的离线无线电。

可达性的一个有效用途是使用它来在网络从离线转换到在线时通知您。此时,您应该重试失败的连接。


3
投票

最好的解决方案是使用qazxsw poi qazxsw poi,用qazxsw poi编写,并使用ReachabilitySwift

简单易用:

class

像魅力一样工作。

请享用


1
投票

更新了juanjo的创建单例实例的答案

Swift 2

用法

SCNetworkReachabilityRef

1
投票

这是在Swift 4.0中

我正在使用这个框架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() 并安装Pod .. 在AppDelegate中

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

如果没有互联网,将显示可访问性ViewController屏幕

© www.soinside.com 2019 - 2024. All rights reserved.