当我试着在我的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)")
}
这些答案中有许多已经不再适用。
原因是使用vpn(因为冠状病毒,我们现在通过vpn而不是公司的wifi进行测试)
使用苹果的网络框架,并基于这里的代码https://medium.com/@udaykiran.munaga/swift-check-for-internet-connectivity-14e355fa10c5,我能够分别检测到wifi和蜂窝网络。由于使用vpn,路径通常保持满意,因此isConnectedToNetwork()总是返回true。
下面的代码使用了apple Network框架,但重新编写,以便在现有代码中仍然使用Reachability.isConnectedToNetwork()。
import Network
class Reachability {
static let shared = Reachability()
let monitorForWifi = NWPathMonitor(requiredInterfaceType: .wifi)
let monitorForCellular = NWPathMonitor(requiredInterfaceType: .cellular)
private var wifiStatus: NWPath.Status = .requiresConnection
private var cellularStatus: NWPath.Status = .requiresConnection
var isReachable: Bool { wifiStatus == .satisfied || isReachableOnCellular }
var isReachableOnCellular: Bool { cellularStatus == .satisfied }
func startMonitoring() {
monitorForWifi.pathUpdateHandler = { [weak self] path in
self?.wifiStatus = path.status
if path.status == .satisfied {
DLog.message("Wifi is connected!")
// post connected notification
} else {
DLog.message("No wifi connection.")
// post disconnected notification
}
}
monitorForCellular.pathUpdateHandler = { [weak self] path in
self?.cellularStatus = path.status
if path.status == .satisfied {
DLog.message("Cellular connection is connected!")
// post connected notification
} else {
DLog.message("No cellular connection.")
// post disconnected notification
}
}
let queue = DispatchQueue(label: "NetworkMonitor")
monitorForCellular.start(queue: queue)
monitorForWifi.start(queue: queue)
}
func stopMonitoring() {
monitorForWifi.cancel()
monitorForCellular.cancel()
}
class func isConnectedToNetwork() -> Bool {
return shared.isReachable
}
}
然后在你的Appdelegate中didFinishLaunchingWithOptions: start monitoring。
Reachability.shared.startMonitoring()
虽然没有直接回答你的问题,但我想提一下苹果公司最近有这样一个谈话:
https://developer.apple.com/videos/play/wwdc2018/714/
大约在9点55分,他谈到了做你正在问的事情:
检查连接
如果连接->做点什么
如果没有连接->执行其他操作(等待?重试?)
然而,这有一些陷阱:
如果在第二步中,它说它有连接,但0.5秒后他就没有了呢?
如果用户在代理的后面呢
最后但并非最不重要的是,如果这里的一些答案不能确定连通性呢?(我敢肯定,如果你快速切换你的连接,去wi-fi,然后关掉它(只是让它变得复杂),它几乎永远不能正确地判断我是否获得了连接)。
视频中说:“没有办法保证未来的行动是否会成功。”
以下是苹果公司的一些最佳实践:
设置waitsForConnectivity为true (https://developer.apple.com/documentation/foundation/urlsessionconfiguration/2908812-waitsforconnectivity)
响应委托方法taskIsWaitingForConnectivity (https://developer.apple.com/documentation/foundation/urlsessiontaskdelegate/2908819-urlsession)。这是苹果推荐的检查连接的方法,正如33:25的视频中提到的那样。
根据谈话,不应该有任何理由预先检查你是否有互联网连接,因为在你向服务器发送请求时,它可能并不准确。
在以下代码的帮助下,您可以检查网络连接的蜂窝网络以及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
}
}
}
如果你的项目有一个高于或等于iOS 12的目标,并使用组合,你可以使用这一小段代码。
import Combine
import Network
enum NerworkType {
case wifi
case cellular
case loopBack
case wired
case other
}
protocol ReachabilityServiceContract {
var reachabilityInfos: PassthroughSubject<NWPath, Never> { get set }
var isNetworkAvailable: CurrentValueSubject<Bool, Never> { get set }
var typeOfCurrentConnection: PassthroughSubject<NerworkType, Never> { get set }
}
final class ReachabilityService: ReachabilityServiceContract {
var reachabilityInfos: PassthroughSubject<NWPath, Never> = .init()
var isNetworkAvailable: CurrentValueSubject<Bool, Never> = .init(false)
var typeOfCurrentConnection: PassthroughSubject<NerworkType, Never> = .init()
private let monitor: NWPathMonitor
private let backgroudQueue = DispatchQueue.global(qos: .background)
init() {
monitor = NWPathMonitor()
setUp()
}
init(with interFaceType: NWInterface.InterfaceType) {
monitor = NWPathMonitor(requiredInterfaceType: interFaceType)
setUp()
}
deinit {
monitor.cancel()
}
}
private extension ReachabilityService {
func setUp() {
monitor.pathUpdateHandler = { [weak self] path in
self?.reachabilityInfos.send(path)
switch path.status {
case .satisfied:
self?.isNetworkAvailable.send(true)
case .unsatisfied, .requiresConnection:
self?.isNetworkAvailable.send(false)
@unknown default:
self?.isNetworkAvailable.send(false)
}
if path.usesInterfaceType(.wifi) {
self?.typeOfCurrentConnection.send(.wifi)
} else if path.usesInterfaceType(.cellular) {
self?.typeOfCurrentConnection.send(.cellular)
} else if path.usesInterfaceType(.loopback) {
self?.typeOfCurrentConnection.send(.loopBack)
} else if path.usesInterfaceType(.wiredEthernet) {
self?.typeOfCurrentConnection.send(.wired)
} else if path.usesInterfaceType(.other) {
self?.typeOfCurrentConnection.send(.other)
}
}
monitor.start(queue: backgroudQueue)
}
}
只要订阅你想要跟踪的变量,你就会得到任何变化的更新。