Skip to content

Instantly share code, notes, and snippets.

@stoeffn
Last active April 21, 2022 18:37
Show Gist options
  • Save stoeffn/07735ef72c36175a866cb04f7cb20d35 to your computer and use it in GitHub Desktop.
Save stoeffn/07735ef72c36175a866cb04f7cb20d35 to your computer and use it in GitHub Desktop.
Network Reachability in Swift—https://www.stoeffn.de/posts/reachability-in-swift/
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"
}
@danomatika
Copy link

Unmanaged<ReachabilityService>

should be

Unmanaged<Reachability>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment