当我试着在我的iPhone上检查网络连接时,我得到了一堆错误。有人能帮我解决这个问题吗?
代码:
import Foundation
import SystemConfiguration
public class Reachability {
class func isConnectedToNetwork() -> Bool {
var zeroAddress = sockaddr_in()
zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress))
zeroAddress.sin_family = sa_family_t(AF_INET)
let defaultRouteReachability = withUnsafePointer(&zeroAddress) {
SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0))
}
var flags: SCNetworkReachabilityFlags = 0
if SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) == 0 {
return false
}
let isReachable = (flags & UInt32(kSCNetworkFlagsReachable)) != 0
let needsConnection = (flags & UInt32(kSCNetworkFlagsConnectionRequired)) != 0
return (isReachable && !needsConnection) ? true : false
}
}
代码的错误:
如果它是不可读的,错误1说:
'Int'不能转换为'SCNetworkReachabilityFlags'
错误2和3:
找不到一个超载的'init'接受提供的参数
我改进了莱科斯的例子。我添加了一些额外的控件来解决双重触发问题,还添加了通知支持来侦听状态更改。
我为防止双重触发问题而添加的控件还显示了设备主要使用哪个连接源来访问互联网。
例如,即使设备同时连接到蜂窝网络和Wi-Fi,“状态”返回为“connectedViaWiFi”,以指示当前的互联网访问是通过Wi-Fi。
import Foundation
import Network
class Reachability {
enum StatusFlag {
case unknow
case noConnection
case connectedViaWiFi
case connectedViaCellular
}
static let connectionStatusHasChangedNotification = NSNotification.Name("Reachability.connectionStatusHasChangedNotification")
static let shared = Reachability()
private var monitorForWifi: NWPathMonitor?
private var monitorForCellular: NWPathMonitor?
private var wifiStatus: NWPath.Status = .requiresConnection
private var cellularStatus: NWPath.Status = .requiresConnection
private var ignoreInitialWiFiStatusUpdate: Bool = true
private var ignoreInitialCelluluarStatusUpdate: Bool = true
private var isReachableOnCellular: Bool { cellularStatus == .satisfied }
private var isReachableOnWiFi: Bool { wifiStatus == .satisfied }
var status: StatusFlag = .unknow {
didSet {
guard status != oldValue else { return }
DispatchQueue.main.async { [weak self] in
NotificationCenter.default.post(name: Self.connectionStatusHasChangedNotification,
object: self?.status)
}
}
}
func startMonitoring() {
monitorForWifi = NWPathMonitor(requiredInterfaceType: .wifi)
monitorForWifi?.pathUpdateHandler = { [weak self] path in
self?.wifiStatus = path.status
self?.ignoreInitialWiFiStatusUpdate = false
self?.updateStatus()
}
monitorForCellular = NWPathMonitor(requiredInterfaceType: .cellular)
monitorForCellular?.pathUpdateHandler = { [weak self] path in
self?.cellularStatus = path.status
self?.ignoreInitialCelluluarStatusUpdate = false
self?.updateStatus()
}
let queue = DispatchQueue.global(qos: .background)
monitorForCellular?.start(queue: queue)
monitorForWifi?.start(queue: queue)
}
func stopMonitoring() {
monitorForWifi?.cancel()
monitorForWifi = nil
monitorForCellular?.cancel()
monitorForCellular = nil
wifiStatus = .requiresConnection
cellularStatus = .requiresConnection
status = .unknow
ignoreInitialWiFiStatusUpdate = true
ignoreInitialCelluluarStatusUpdate = true
}
private func updateStatus() {
if ignoreInitialWiFiStatusUpdate || ignoreInitialCelluluarStatusUpdate {
return
}
if !(isReachableOnCellular && isReachableOnWiFi) {
if isReachableOnCellular && !isReachableOnWiFi {
status = .connectedViaCellular
} else if isReachableOnWiFi && !isReachableOnCellular {
status = .connectedViaWiFi
} else {
status = .noConnection
}
} else {
status = .connectedViaWiFi
}
}
static func isConnectedToNetwork() -> Bool {
return shared.isReachableOnCellular || shared.isReachableOnWiFi
}
}
示例使用
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(reachabilityChanged(_:)), name: Reachability.connectionStatusHasChangedNotification, object: nil)
Reachability.shared.startMonitoring()
}
@objc func reachabilityChanged(_ sender: Notification) {
guard let statusFlag = sender.object as? Reachability.StatusFlag else { return }
print("TEST -> statusFlag: \(statusFlag)")
}
There is no real way to do this, even APIs that do this will try to connect to a particular service and tell you if it succeed after previously failing, the best approach is to just try to connect to your service and handle the error appropriately, if you want some service that will notify you when you become connected, all that will happens is that it periodically calls to a fixed service until it gets a response and then let you know that it succeed, what if the problem is not the internet itself but the specific service you are trying to connect to.
最好的方法是在你的连接方法中设计处理这个问题的方法,你可以在给定的时间内触发重试,这取决于你的错误,也许是失败的数量,你可以返回一个错误,你可以用它来显示一个错误消息,给用户一个重试的可能性,也许是尝试几次,然后返回和错误的组合。
另一个类似的问题是发送你已经发送但还没有得到响应的消息,最好还是写你的应用程序来处理这个,忽略请求,直到它等待的请求返回,如果请求不同,取消等待请求,等等
这些东西可以以非常通用的方式编写,所以它们可以用于应用程序的许多不同方面,甚至不同的项目。
在以下代码的帮助下,您可以检查网络连接的蜂窝网络以及wifi。
Swift 3.0
import UIKit
import Foundation
import SystemConfiguration
class NetworkConnection: UIViewController {
class func isConnectedToNetwork() -> 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(MemoryLayout.size(ofValue: zeroAddress))
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 = SCNetworkReachabilityFlags(rawValue: 0)
if SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) == false {
return false
}
let isReachable = (flags.rawValue & UInt32(kSCNetworkFlagsReachable)) != 0
let needsConnection = (flags.rawValue & UInt32(kSCNetworkFlagsConnectionRequired)) != 0
return (isReachable && !needsConnection)
}
class func checkConnection(sender:UIViewController){
if NetworkConnection.isConnectedToNetwork() == true {
print("Connected to the internet")
// Do something
} else {
print("No internet connection")
let alertController = UIAlertController(title: "No Internet Available", message: "", preferredStyle: UIAlertControllerStyle.alert)
let okAction = UIAlertAction(title: "Ok", style: UIAlertActionStyle.default){(result:UIAlertAction) -> Void in
return
}
alertController.addAction(okAction)
sender.present(alertController, animated: true, completion: nil)
// Do something
}
}
}
更新版本的@martin的答案Swift 5+使用组合。它还包括iOS 14的不可用原因检查。
import Combine
import Network
enum NetworkType {
case wifi
case cellular
case loopBack
case wired
case other
}
final class ReachabilityService: ObservableObject {
@Published var reachabilityInfos: NWPath?
@Published var isNetworkAvailable: Bool?
@Published var typeOfCurrentConnection: NetworkType?
private let monitor = NWPathMonitor()
private let backgroundQueue = DispatchQueue.global(qos: .background)
init() {
setUp()
}
init(with interFaceType: NWInterface.InterfaceType) {
setUp()
}
deinit {
monitor.cancel()
}
}
private extension ReachabilityService {
func setUp() {
monitor.pathUpdateHandler = { [weak self] path in
self?.reachabilityInfos = path
switch path.status {
case .satisfied:
print("ReachabilityService: satisfied")
self?.isNetworkAvailable = true
break
case .unsatisfied:
print("ReachabilityService: unsatisfied")
if #available(iOS 14.2, *) {
switch path.unsatisfiedReason {
case .notAvailable:
print("ReachabilityService: unsatisfiedReason: notAvailable")
break
case .cellularDenied:
print("ReachabilityService: unsatisfiedReason: cellularDenied")
break
case .wifiDenied:
print("ReachabilityService: unsatisfiedReason: wifiDenied")
break
case .localNetworkDenied:
print("ReachabilityService: unsatisfiedReason: localNetworkDenied")
break
@unknown default:
print("ReachabilityService: unsatisfiedReason: default")
}
} else {
// Fallback on earlier versions
}
self?.isNetworkAvailable = false
break
case .requiresConnection:
print("ReachabilityService: requiresConnection")
self?.isNetworkAvailable = false
break
@unknown default:
print("ReachabilityService: default")
self?.isNetworkAvailable = false
}
if path.usesInterfaceType(.wifi) {
self?.typeOfCurrentConnection = .wifi
} else if path.usesInterfaceType(.cellular) {
self?.typeOfCurrentConnection = .cellular
} else if path.usesInterfaceType(.loopback) {
self?.typeOfCurrentConnection = .loopBack
} else if path.usesInterfaceType(.wiredEthernet) {
self?.typeOfCurrentConnection = .wired
} else if path.usesInterfaceType(.other) {
self?.typeOfCurrentConnection = .other
}
}
monitor.start(queue: backgroundQueue)
}
}
用法:
在视图模型中:
private let reachability = ReachabilityService()
init() {
reachability.$isNetworkAvailable.sink { [weak self] isConnected in
self?.isConnected = isConnected ?? false
}.store(in: &cancelBag)
}
在你的控制器中:
viewModel.$isConnected.sink { [weak self] isConnected in
print("isConnected: \(isConnected)")
DispatchQueue.main.async {
//Update your UI in here
}
}.store(in: &bindings)
这是我的版本。本质上,它没有带来任何新东西。我把它绑定到UIDevice。
import UIKit
import SystemConfiguration
extension UIDevice {
open class var isConnectedToNetwork: Bool {
get {
var zeroAddress = sockaddr_in()
zeroAddress.sin_len = UInt8(MemoryLayout.size(ofValue: zeroAddress))
zeroAddress.sin_family = sa_family_t(AF_INET)
guard
let defaultRouteReachability: SCNetworkReachability = withUnsafePointer(to: &zeroAddress, {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
SCNetworkReachabilityCreateWithAddress(nil, $0)
}
}),
var flags: SCNetworkReachabilityFlags = SCNetworkReachabilityFlags() as SCNetworkReachabilityFlags?,
SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags)
else { return false }
return flags.contains(.reachable) && !flags.contains(.connectionRequired)
}
}
}
print("Network status availability: " + ( UIDevice.isConnectedToNetwork ? "true" : "false" ))