I downloaded the iOS sample Reachability project from AshleyMills Reachability
It has a networkStatus
label that says either: Wifi
or No Connection
Underneath that it has a hostNameLabel
that says either: No host name
or google.com
or invalidhost
When I run the app on my device it repeatedly cycles the connection every 5 seconds in the following manner:
1st 5 secs:
2nd 5 sec:
3rd 5 secs:
Start cycle over from 1st, 2nd, and 3rd all over again and keep repeating.
I'm on Home wifi and my internet connection is strong (no problems with any other apps). Why does it continuously cycle like this instead of just staying on Wifi
+ google.com
(the 2nd cycle)
ViewController File:
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()
}
}
Reachability File:
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()
}
}
}
I looked through the code and found the answer. Inside the
ViewController
file thestartHost()
function has timer in it that cycles every 5 secs.