我试图将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中是不可能的。所以我现在真的迷失了该怎么做。
我读过Apple的官方document关于在Swift中与C API进行交互但是它没有使用结构的例子。
有人可以帮帮我吗?我真的很感激。
谢谢。
更新:感谢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
}
编辑1:好的我将此行更改为
var defaultRouteReachability: SCNetworkReachabilityRef = SCNetworkReachabilityCreateWithAddress(UnsafePointer<Void>(), &zeroAddress)
我在这一行得到的新错误是&#39; UnsafePointer&#39;不能转换为&#39; CFAllocator&#39; 。如何在Swift中传递NULL
?
我也改变了这一行,错误现在消失了。
let didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags)
编辑2:在看到this问题后,我在此行中通过了nil
。但这个答案与答案here相矛盾。它说在Swift中没有等同于NULL
。
var defaultRouteReachability: SCNetworkReachabilityRef = SCNetworkReachabilityCreateWithAddress(nil, &zeroAddress)
无论如何,我收到一条新错误,上面写着&#39; sockaddr_in&#39;与上面一行的&#39; sockaddr&#39; 不同。
答案 0 :(得分:230)
(由于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()
给出了这个结构的大小
要转换为UInt8
sin_len
:
zeroAddress.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
块和函数
回报。
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 Swift和Swift 2 - UnsafeMutablePointer<Void> to object。
Swift 3/4的更新:
不安全的指针不能简单地转换为a的指针 不同类型了(见 - 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)
}
答案 1 :(得分: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)
}
})
}
答案 2 :(得分:6)
这与Swift无关,但最好的解决方案是不使用Reachability来确定网络是否在线。如果失败,只需建立连接并处理错误。建立连接有时会启动休眠的离线无线电。
可达性的一个有效用途是使用它来在网络从离线转换到在线时通知您。此时,您应该重试失败的连接。
答案 3 :(得分:4)
最佳解决方案是使用ReachabilitySwift
中编写的Swift 2
class,并使用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()
像魅力一样工作。
享受
答案 4 :(得分:1)
更新了juanjo创建单例实例的答案
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(){
}
答案 5 :(得分:0)
这是在Swift 4.0中
我正在使用此框架https://github.com/ashleymills/Reachability.swift
并安装Pod ..
在 AppDelegate
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")
}
}
}
如果没有互联网,则会出现屏幕
答案 6 :(得分:0)
快速5,使用NWPathMonitor
import Network
func configureNetworkMonitor(){
let monitor = NWPathMonitor()
monitor.pathUpdateHandler = { path in
if path.status != .satisfied {
print("not connected")
}
else if path.usesInterfaceType(.cellular) {
print("Cellular")
}
else if path.usesInterfaceType(.wifi) {
print("WIFI")
}
else if path.usesInterfaceType(.wiredEthernet) {
print("Ethernet")
}
else if path.usesInterfaceType(.other){
print("Other")
}else if path.usesInterfaceType(.loopback){
print("Loop Back")
}
}
monitor.start(queue: DispatchQueue.global(qos: .background))
}
答案 7 :(得分:0)
SwiftUI 采用 Mithra Sigam 的上述解决方案:
import Foundation
import Network
class NetworkReachabilityManager: ObservableObject {
@Published var networkPathStatus: NWPath.Status
@Published var availableInterfaces: [NWInterface]
let monitor = NWPathMonitor()
init() {
monitor.start(queue: DispatchQueue.global(qos: .background))
let currentPath = monitor.currentPath
networkPathStatus = currentPath.status
availableInterfaces = currentPath.availableInterfaces
monitor.pathUpdateHandler = { [self] networkPath in
networkPathStatus = networkPath.status
availableInterfaces = networkPath.availableInterfaces
}
}
deinit {
monitor.cancel()
}
}