Swift iOS -AshleyMills Reachability 每隔几秒就会打开和关闭 wifi

Posted

技术标签:

【中文标题】Swift iOS -AshleyMills Reachability 每隔几秒就会打开和关闭 wifi【英文标题】:Swift iOS -AshleyMills Reachability keeps toggling wifi on and off every few secs 【发布时间】:2018-05-03 17:49:50 【问题描述】:

我从AshleyMills Reachability下载了ios示例Reachability项目

它有一个 networkStatus 标签,上面写着:WifiNo Connection

在它下面有一个 hostNameLabel,上面写着:No host namegoogle.cominvalidhost

当我在我的设备上运行该应用程序时,它每 5 秒以下列方式重复循环连接:

第一个 5 秒:

第二个 5 秒:

第三个​​ 5 秒:

从第 1 次、第 2 次和第 3 次重新开始循环并不断重复。

我使用的是家庭 wifi,并且我的互联网连接很好(任何其他应用程序都没有问题)。 为什么它会像这样不断循环,而不是停留在Wifi + google.com(第二个循环)

ViewController 文件:

class ViewController: UIViewController 

    let networkStatus: UILabel = 
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.font = UIFont.systemFont(ofSize: 21)
        label.textColor = .black
        label.numberOfLines = 0
        label.sizeToFit()
        label.text = "Status"
        label.textAlignment = .center
        return label
    ()

    let hostNameLabel: UILabel = 
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.font = UIFont.systemFont(ofSize: 21)
        label.textColor = .black
        label.numberOfLines = 0
        label.sizeToFit()
        label.text = "Host"
        label.textAlignment = .center
        return label
    ()

    var reachability: Reachability?
    let hostNames = [nil, "google.com", "invalidhost"]
    var hostIndex = 0

    override func viewDidLoad() 
        super.viewDidLoad()

        setConstraints()
        startHost(at: 0)
    

    func startHost(at index: Int) 
        stopNotifier()
        setupReachability(hostNames[index], useClosures: true)
        startNotifier()
        DispatchQueue.main.asyncAfter(deadline: .now() + 5) 
            self.startHost(at: (index + 1) % 3)
        
    

    func setupReachability(_ hostName: String?, useClosures: Bool) 
        let reachability: Reachability?
        if let hostName = hostName 
            reachability = Reachability(hostname: hostName)
            hostNameLabel.text = hostName
         else 
            reachability = Reachability()
            hostNameLabel.text = "No host name"
        
        self.reachability = reachability
        print("--- set up with host name: \(hostNameLabel.text!)")

        if useClosures 
            reachability?.whenReachable =  reachability in
                self.updateLabelColourWhenReachable(reachability)
            
            reachability?.whenUnreachable =  reachability in
                self.updateLabelColourWhenNotReachable(reachability)
            
         else 
            NotificationCenter.default.addObserver(
                self,
                selector: #selector(reachabilityChanged(_:)),
                name: .reachabilityChanged,
                object: reachability
            )
        
    

    func startNotifier() 
        print("--- start notifier")
        do 
            try reachability?.startNotifier()
         catch 
            networkStatus.textColor = .red
            networkStatus.text = "Unable to start\nnotifier"
            return
        
    

    func stopNotifier() 
        print("--- stop notifier")
        reachability?.stopNotifier()
        NotificationCenter.default.removeObserver(self, name: .reachabilityChanged, object: nil)
        reachability = nil
    

    func updateLabelColourWhenReachable(_ reachability: Reachability) 
        print("\(reachability.description) - \(reachability.connection)")
        if reachability.connection == .wifi 
            self.networkStatus.textColor = .green
         else 
            self.networkStatus.textColor = .blue
        

        self.networkStatus.text = "\(reachability.connection)"
    

    func updateLabelColourWhenNotReachable(_ reachability: Reachability) 
        print("\(reachability.description) - \(reachability.connection)")

        self.networkStatus.textColor = .red

        self.networkStatus.text = "\(reachability.connection)"
    

    @objc func reachabilityChanged(_ note: Notification) 
        let reachability = note.object as! Reachability

        if reachability.connection != .none 
            updateLabelColourWhenReachable(reachability)
         else 
            updateLabelColourWhenNotReachable(reachability)
        
    

    func setConstraints()
        view.addSubview(networkStatus)
        view.addSubview(hostNameLabel)

        networkStatus.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 8).isActive = true
        networkStatus.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -8).isActive = true
        networkStatus.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true

        hostNameLabel.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 8).isActive = true
        hostNameLabel.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -8).isActive = true
        hostNameLabel.topAnchor.constraint(equalTo: networkStatus.bottomAnchor, constant: 20).isActive = true
    

    deinit 
        stopNotifier()
    

可达性文件:

import SystemConfiguration
import Foundation

public enum ReachabilityError: Error 
    case FailedToCreateWithAddress(sockaddr_in)
    case FailedToCreateWithHostname(String)
    case UnableToSetCallback
    case UnableToSetDispatchQueue


@available(*, unavailable, renamed: "Notification.Name.reachabilityChanged")
public let ReachabilityChangedNotification = NSNotification.Name("ReachabilityChangedNotification")

extension Notification.Name 
    public static let reachabilityChanged = Notification.Name("reachabilityChanged")


func callback(reachability: SCNetworkReachability, flags: SCNetworkReachabilityFlags, info: UnsafeMutableRawPointer?) 
    guard let info = info else  return 

    let reachability = Unmanaged<Reachability>.fromOpaque(info).takeUnretainedValue()
    reachability.reachabilityChanged()


public class Reachability 

    public typealias NetworkReachable = (Reachability) -> ()
    public typealias NetworkUnreachable = (Reachability) -> ()

    @available(*, unavailable, renamed: "Connection")
    public enum NetworkStatus: CustomStringConvertible 
        case notReachable, reachableViaWiFi, reachableViaWWAN
        public var description: String 
            switch self 
            case .reachableViaWWAN: return "Cellular"
            case .reachableViaWiFi: return "WiFi"
            case .notReachable: return "No Connection"
            
        
    

    public enum Connection: CustomStringConvertible 
        case none, wifi, cellular
        public var description: String 
            switch self 
            case .cellular: return "Cellular"
            case .wifi: return "WiFi"
            case .none: return "No Connection"
            
        
    

    public var whenReachable: NetworkReachable?
    public var whenUnreachable: NetworkUnreachable?

    @available(*, deprecated: 4.0, renamed: "allowsCellularConnection")
    public let reachableOnWWAN: Bool = true

    /// Set to `false` to force Reachability.connection to .none when on cellular connection (default value `true`)
    public var allowsCellularConnection: Bool

    // The notification center on which "reachability changed" events are being posted
    public var notificationCenter: NotificationCenter = NotificationCenter.default

    @available(*, deprecated: 4.0, renamed: "connection.description")
    public var currentReachabilityString: String 
        return "\(connection)"
    

    @available(*, unavailable, renamed: "connection")
    public var currentReachabilityStatus: Connection 
        return connection
    

    public var connection: Connection 
        guard isReachableFlagSet else  return .none 

        // If we're reachable, but not on an iOS device (i.e. simulator), we must be on WiFi
        guard isRunningOnDevice else  return .wifi 

        var connection = Connection.none

        if !isConnectionRequiredFlagSet 
            connection = .wifi
        

        if isConnectionOnTrafficOrDemandFlagSet 
            if !isInterventionRequiredFlagSet 
                connection = .wifi
            
        

        if isOnWWANFlagSet 
            if !allowsCellularConnection 
                connection = .none
             else 
                connection = .cellular
            
        

        return connection
    

    fileprivate var previousFlags: SCNetworkReachabilityFlags?

    fileprivate var isRunningOnDevice: Bool = 
        #if targetEnvironment(simulator)
        return false
        #else
        return true
        #endif
    ()

    fileprivate var notifierRunning = false
    fileprivate let reachabilityRef: SCNetworkReachability

    fileprivate let reachabilitySerialQueue = DispatchQueue(label: "uk.co.ashleymills.reachability")

    fileprivate var usingHostname = false

    required public init(reachabilityRef: SCNetworkReachability, usingHostname: Bool = false) 
        allowsCellularConnection = true
        self.reachabilityRef = reachabilityRef
        self.usingHostname = usingHostname
    

    public convenience init?(hostname: String) 
        guard let ref = SCNetworkReachabilityCreateWithName(nil, hostname) else  return nil 
        self.init(reachabilityRef: ref, usingHostname: true)
    

    public convenience init?() 
        var zeroAddress = sockaddr()
        zeroAddress.sa_len = UInt8(MemoryLayout<sockaddr>.size)
        zeroAddress.sa_family = sa_family_t(AF_INET)

        guard let ref = SCNetworkReachabilityCreateWithAddress(nil, &zeroAddress) else  return nil 

        self.init(reachabilityRef: ref)
    

    deinit 
        stopNotifier()
    


public extension Reachability 

    // MARK: - *** Notifier methods ***
    func startNotifier() throws 
        guard !notifierRunning else  return 

        var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
        context.info = UnsafeMutableRawPointer(Unmanaged<Reachability>.passUnretained(self).toOpaque())
        if !SCNetworkReachabilitySetCallback(reachabilityRef, callback, &context) 
            stopNotifier()
            throw ReachabilityError.UnableToSetCallback
        

        if !SCNetworkReachabilitySetDispatchQueue(reachabilityRef, reachabilitySerialQueue) 
            stopNotifier()
            throw ReachabilityError.UnableToSetDispatchQueue
        

        // Perform an initial check
        reachabilitySerialQueue.async 
            self.reachabilityChanged()
        

        notifierRunning = true
    

    func stopNotifier() 
        defer  notifierRunning = false 

        SCNetworkReachabilitySetCallback(reachabilityRef, nil, nil)
        SCNetworkReachabilitySetDispatchQueue(reachabilityRef, nil)
    

    // MARK: - *** Connection test methods ***
    @available(*, deprecated: 4.0, message: "Please use `connection != .none`")
    var isReachable: Bool 
        guard isReachableFlagSet else  return false 

        if isConnectionRequiredAndTransientFlagSet 
            return false
        

        if isRunningOnDevice 
            if isOnWWANFlagSet && !reachableOnWWAN 
                // We don't want to connect when on cellular connection
                return false
            
        

        return true
    

    @available(*, deprecated: 4.0, message: "Please use `connection == .cellular`")
    var isReachableViaWWAN: Bool 
        // Check we're not on the simulator, we're REACHABLE and check we're on WWAN
        return isRunningOnDevice && isReachableFlagSet && isOnWWANFlagSet
    

    @available(*, deprecated: 4.0, message: "Please use `connection == .wifi`")
    var isReachableViaWiFi: Bool 
        // Check we're reachable
        guard isReachableFlagSet else  return false 

        // If reachable we're reachable, but not on an iOS device (i.e. simulator), we must be on WiFi
        guard isRunningOnDevice else  return true 

        // Check we're NOT on WWAN
        return !isOnWWANFlagSet
    

    var description: String 
        let W = isRunningOnDevice ? (isOnWWANFlagSet ? "W" : "-") : "X"
        let R = isReachableFlagSet ? "R" : "-"
        let c = isConnectionRequiredFlagSet ? "c" : "-"
        let t = isTransientConnectionFlagSet ? "t" : "-"
        let i = isInterventionRequiredFlagSet ? "i" : "-"
        let C = isConnectionOnTrafficFlagSet ? "C" : "-"
        let D = isConnectionOnDemandFlagSet ? "D" : "-"
        let l = isLocalAddressFlagSet ? "l" : "-"
        let d = isDirectFlagSet ? "d" : "-"

        return "\(W)\(R) \(c)\(t)\(i)\(C)\(D)\(l)\(d)"
    


fileprivate extension Reachability 
    func reachabilityChanged() 
        guard previousFlags != flags else  return 

        let block = connection != .none ? whenReachable : whenUnreachable

        DispatchQueue.main.async 
            if self.usingHostname 
                print("USING HOSTNAME ABOUT TO CALL BLOCK")
            
            block?(self)
            self.notificationCenter.post(name: .reachabilityChanged, object:self)
        

        previousFlags = flags
    

    var isOnWWANFlagSet: Bool 
        #if os(iOS)
        return flags.contains(.isWWAN)
        #else
        return false
        #endif
    
    var isReachableFlagSet: Bool 
        return flags.contains(.reachable)
    
    var isConnectionRequiredFlagSet: Bool 
        return flags.contains(.connectionRequired)
    
    var isInterventionRequiredFlagSet: Bool 
        return flags.contains(.interventionRequired)
    
    var isConnectionOnTrafficFlagSet: Bool 
        return flags.contains(.connectionOnTraffic)
    
    var isConnectionOnDemandFlagSet: Bool 
        return flags.contains(.connectionOnDemand)
    
    var isConnectionOnTrafficOrDemandFlagSet: Bool 
        return !flags.intersection([.connectionOnTraffic, .connectionOnDemand]).isEmpty
    
    var isTransientConnectionFlagSet: Bool 
        return flags.contains(.transientConnection)
    
    var isLocalAddressFlagSet: Bool 
        return flags.contains(.isLocalAddress)
    
    var isDirectFlagSet: Bool 
        return flags.contains(.isDirect)
    
    var isConnectionRequiredAndTransientFlagSet: Bool 
        return flags.intersection([.connectionRequired, .transientConnection]) == [.connectionRequired, .transientConnection]
    

    var flags: SCNetworkReachabilityFlags 
        var flags = SCNetworkReachabilityFlags()
        if SCNetworkReachabilityGetFlags(reachabilityRef, &flags) 
            print("Returning flags \(flags)")
            return flags
         else 
            return SCNetworkReachabilityFlags()
        
    

【问题讨论】:

这只是一个测试应用程序,演示了各种类型的连接会发生什么。首先没有主机,然后是 google.com ,然后是无效的主机名 我想通了。我实际上是在使用您的代码的另一个项目中使用它。它工作得很好。谢谢! 【参考方案1】:

我查看了代码并找到了答案。在ViewController 文件中,startHost() 函数中有一个定时器,每 5 秒循环一次。

func startHost(at index: Int) 
        stopNotifier()
        setupReachability(hostNames[index], useClosures: true)
        startNotifier()

        // Timer is HERE
        DispatchQueue.main.asyncAfter(deadline: .now() + 5) 
            self.startHost(at: (index + 1) % 3)
        
    

【讨论】:

以上是关于Swift iOS -AshleyMills Reachability 每隔几秒就会打开和关闭 wifi的主要内容,如果未能解决你的问题,请参考以下文章

iOS当我滚动到底部Swift 4时如何更新表格视图数据

UIImage 显示为不透明的正方形(IOS Swift)

Swift - 失败:Alamofire 中的 responseValidationFailed(状态码 401)

在 iOS 设置中更改通知时,Amazon SNS 如何禁用/重新启用设备?

ios 整理(一)swift和oc的区别

Re: 快速从 UIImage/CGImage 获取像素数据作为数组