Last active
April 21, 2022 18:37
-
-
Save stoeffn/07735ef72c36175a866cb04f7cb20d35 to your computer and use it in GitHub Desktop.
Network Reachability in Swift—https://www.stoeffn.de/posts/reachability-in-swift/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SystemConfiguration | |
/// Service that provides information on network reachability with the ability to watch for changes and post corresponding | |
/// notifications. | |
public final class Reachability { | |
private var reachability: SCNetworkReachability | |
// MARK: - Life Cycle | |
/// Creates a new reachability service for the host given. | |
/// | |
/// - Remark: In order to start watching for reachability changes, you must set `isActive` to `true`. | |
init(host: String = "apple.com") { | |
guard let reachability = SCNetworkReachabilityCreateWithName(nil, host) else { | |
fatalError("Cannot create reachability service for host '\(host)' because `SCNetworkReachabilityCreateWithName` failed.") | |
} | |
self.reachability = reachability | |
self.update() | |
} | |
deinit { | |
deactivate() | |
} | |
// MARK: - Retrieving and Watching Reachability | |
/// Current reachability flags. | |
/// | |
/// If `isActive` is set to `false`, these flags may not be up-to-date. You can manually update them by calling `update()`. | |
public private(set) var currentFlags: SCNetworkReachabilityFlags = [] | |
/// Activates or deactivates automatically watching network reachability. | |
/// | |
/// Changes will be posted as `Notification.Name.reachabilityDidChange`. | |
public var isActive: Bool = false { | |
didSet { | |
guard isActive != oldValue else { return } | |
isActive ? activate() : deactivate() | |
} | |
} | |
/// Manually updates the current reachability flags. May trigger a notification. | |
public func update() { | |
var flags = SCNetworkReachabilityFlags() | |
SCNetworkReachabilityGetFlags(reachability, &flags) | |
reachabilityChanged(flags: flags) | |
} | |
private func activate() { | |
let selfReference = UnsafeMutableRawPointer(Unmanaged<ReachabilityService>.passUnretained(self).toOpaque()) | |
var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil) | |
context.info = selfReference | |
let reachabilityCallback: SCNetworkReachabilityCallBack? = { _, flags, info in | |
guard let info = info else { return } | |
Unmanaged<ReachabilityService>.fromOpaque(info).takeUnretainedValue().reachabilityChanged(flags: flags) | |
} | |
if !SCNetworkReachabilitySetCallback(reachability, reachabilityCallback, &context) { | |
fatalError("Cannot activate reachability service because `SCNetworkReachabilitySetCallback` failed.") | |
} | |
if !SCNetworkReachabilitySetDispatchQueue(reachability, .main) { | |
fatalError("Cannot activate reachability service because `SCNetworkReachabilitySetDispatchQueue` failed.") | |
} | |
update() | |
} | |
private func deactivate() { | |
SCNetworkReachabilitySetCallback(reachability, nil, nil) | |
SCNetworkReachabilitySetDispatchQueue(reachability, nil) | |
} | |
private func reachabilityChanged(flags: SCNetworkReachabilityFlags) { | |
guard currentFlags != flags else { return } | |
currentFlags = flags | |
NotificationCenter.default.post(name: .reachabilityDidChange, object: self, userInfo: [ | |
Notification.Name.reachabilityDidChangeFlagsKey: currentFlags, | |
]) | |
} | |
// MARK: - Shared Instance | |
/// Shared reachability service. | |
/// | |
/// - Remark: Please note that you must set `isActive` to `true` in order to receive notifications. | |
public static let shared = Reachability() | |
} | |
// MARK: - Notifications | |
extension Notification.Name { | |
/// Invoked when network reachability changed. User info will contain the new reachability flags keyed by | |
/// `reachabilityChangedFlagsKey`. | |
/// | |
/// - Remark: You need to create and activate a `ReachabilityService` first in order to start receiving notifications. | |
public static let reachabilityDidChange = Notification.Name(rawValue: "ReachabilityDidChange") | |
/// User info key for `reachabilityChanged` flags. | |
public static let reachabilityDidChangeFlagsKey = "flags" | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
should be