Delete more unused code from Reachability.
This commit is contained in:
parent
3b59ffc446
commit
7311c25d35
|
@ -29,10 +29,7 @@ import SystemConfiguration
|
|||
import Foundation
|
||||
|
||||
public enum ReachabilityError: Error {
|
||||
case failedToCreateWithAddress(sockaddr, Int32)
|
||||
case failedToCreateWithHostname(String, Int32)
|
||||
case unableToSetCallback(Int32)
|
||||
case unableToSetDispatchQueue(Int32)
|
||||
case failedToCreateWithHostname(String, Int32)
|
||||
case unableToGetFlags(Int32)
|
||||
}
|
||||
|
||||
|
@ -96,132 +93,31 @@ public class Reachability {
|
|||
}
|
||||
}
|
||||
|
||||
fileprivate(set) var notifierRunning = false
|
||||
fileprivate let reachabilityRef: SCNetworkReachability
|
||||
fileprivate let reachabilitySerialQueue: DispatchQueue
|
||||
fileprivate let notificationQueue: DispatchQueue?
|
||||
fileprivate(set) var flags: SCNetworkReachabilityFlags?
|
||||
fileprivate(set) var notifierRunning = false
|
||||
fileprivate let reachabilityRef: SCNetworkReachability
|
||||
fileprivate let reachabilitySerialQueue: DispatchQueue
|
||||
fileprivate(set) var flags: SCNetworkReachabilityFlags?
|
||||
|
||||
required public init(reachabilityRef: SCNetworkReachability,
|
||||
queueQoS: DispatchQoS = .default,
|
||||
targetQueue: DispatchQueue? = nil,
|
||||
notificationQueue: DispatchQueue? = .main) {
|
||||
self.allowsCellularConnection = true
|
||||
self.reachabilityRef = reachabilityRef
|
||||
self.reachabilitySerialQueue = DispatchQueue(label: "uk.co.ashleymills.reachability", qos: queueQoS, target: targetQueue)
|
||||
self.notificationQueue = notificationQueue
|
||||
}
|
||||
required public init(reachabilityRef: SCNetworkReachability,
|
||||
queueQoS: DispatchQoS = .default,
|
||||
targetQueue: DispatchQueue? = nil) {
|
||||
self.allowsCellularConnection = true
|
||||
self.reachabilityRef = reachabilityRef
|
||||
self.reachabilitySerialQueue = DispatchQueue(label: "uk.co.ashleymills.reachability", qos: queueQoS, target: targetQueue)
|
||||
}
|
||||
|
||||
public convenience init(hostname: String,
|
||||
queueQoS: DispatchQoS = .default,
|
||||
targetQueue: DispatchQueue? = nil,
|
||||
notificationQueue: DispatchQueue? = .main) throws {
|
||||
guard let ref = SCNetworkReachabilityCreateWithName(nil, hostname) else {
|
||||
throw ReachabilityError.failedToCreateWithHostname(hostname, SCError())
|
||||
}
|
||||
self.init(reachabilityRef: ref, queueQoS: queueQoS, targetQueue: targetQueue, notificationQueue: notificationQueue)
|
||||
}
|
||||
|
||||
public convenience init(queueQoS: DispatchQoS = .default,
|
||||
targetQueue: DispatchQueue? = nil,
|
||||
notificationQueue: DispatchQueue? = .main) throws {
|
||||
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 {
|
||||
throw ReachabilityError.failedToCreateWithAddress(zeroAddress, SCError())
|
||||
}
|
||||
|
||||
self.init(reachabilityRef: ref, queueQoS: queueQoS, targetQueue: targetQueue, notificationQueue: notificationQueue)
|
||||
}
|
||||
|
||||
deinit {
|
||||
stopNotifier()
|
||||
}
|
||||
public convenience init(hostname: String,
|
||||
queueQoS: DispatchQoS = .default,
|
||||
targetQueue: DispatchQueue? = nil) throws {
|
||||
guard let ref = SCNetworkReachabilityCreateWithName(nil, hostname) else {
|
||||
throw ReachabilityError.failedToCreateWithHostname(hostname, SCError())
|
||||
}
|
||||
self.init(reachabilityRef: ref, queueQoS: queueQoS, targetQueue: targetQueue)
|
||||
}
|
||||
}
|
||||
|
||||
public extension Reachability {
|
||||
|
||||
// MARK: - *** Notifier methods ***
|
||||
func startNotifier() throws {
|
||||
guard !notifierRunning else { return }
|
||||
|
||||
let callback: SCNetworkReachabilityCallBack = { (reachability, flags, info) in
|
||||
guard let info = info else { return }
|
||||
|
||||
// `weakifiedReachability` is guaranteed to exist by virtue of our
|
||||
// retain/release callbacks which we provided to the `SCNetworkReachabilityContext`.
|
||||
let weakifiedReachability = Unmanaged<ReachabilityWeakifier>.fromOpaque(info).takeUnretainedValue()
|
||||
|
||||
// The weak `reachability` _may_ no longer exist if the `Reachability`
|
||||
// object has since been deallocated but a callback was already in flight.
|
||||
weakifiedReachability.reachability?.flags = flags
|
||||
}
|
||||
|
||||
let weakifiedReachability = ReachabilityWeakifier(reachability: self)
|
||||
let opaqueWeakifiedReachability = Unmanaged<ReachabilityWeakifier>.passUnretained(weakifiedReachability).toOpaque()
|
||||
|
||||
var context = SCNetworkReachabilityContext(
|
||||
version: 0,
|
||||
info: UnsafeMutableRawPointer(opaqueWeakifiedReachability),
|
||||
retain: { (info: UnsafeRawPointer) -> UnsafeRawPointer in
|
||||
let unmanagedWeakifiedReachability = Unmanaged<ReachabilityWeakifier>.fromOpaque(info)
|
||||
_ = unmanagedWeakifiedReachability.retain()
|
||||
return UnsafeRawPointer(unmanagedWeakifiedReachability.toOpaque())
|
||||
},
|
||||
release: { (info: UnsafeRawPointer) -> Void in
|
||||
let unmanagedWeakifiedReachability = Unmanaged<ReachabilityWeakifier>.fromOpaque(info)
|
||||
unmanagedWeakifiedReachability.release()
|
||||
},
|
||||
copyDescription: { (info: UnsafeRawPointer) -> Unmanaged<CFString> in
|
||||
let unmanagedWeakifiedReachability = Unmanaged<ReachabilityWeakifier>.fromOpaque(info)
|
||||
let weakifiedReachability = unmanagedWeakifiedReachability.takeUnretainedValue()
|
||||
let description = weakifiedReachability.reachability?.description ?? "nil"
|
||||
return Unmanaged.passRetained(description as CFString)
|
||||
}
|
||||
)
|
||||
|
||||
if !SCNetworkReachabilitySetCallback(reachabilityRef, callback, &context) {
|
||||
stopNotifier()
|
||||
throw ReachabilityError.unableToSetCallback(SCError())
|
||||
}
|
||||
|
||||
if !SCNetworkReachabilitySetDispatchQueue(reachabilityRef, reachabilitySerialQueue) {
|
||||
stopNotifier()
|
||||
throw ReachabilityError.unableToSetDispatchQueue(SCError())
|
||||
}
|
||||
|
||||
// Perform an initial check
|
||||
try setReachabilityFlags()
|
||||
|
||||
notifierRunning = true
|
||||
}
|
||||
|
||||
func stopNotifier() {
|
||||
defer { notifierRunning = false }
|
||||
|
||||
SCNetworkReachabilitySetCallback(reachabilityRef, nil, nil)
|
||||
SCNetworkReachabilitySetDispatchQueue(reachabilityRef, nil)
|
||||
}
|
||||
|
||||
// MARK: - *** Connection test methods ***
|
||||
@available(*, deprecated, message: "Please use `connection != .none`")
|
||||
var isReachable: Bool {
|
||||
return connection != .unavailable
|
||||
}
|
||||
|
||||
@available(*, deprecated, 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 connection == .cellular
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "Please use `connection == .wifi`")
|
||||
var isReachableViaWiFi: Bool {
|
||||
return connection == .wifi
|
||||
}
|
||||
|
||||
var description: String {
|
||||
return flags?.description ?? "unavailable flags"
|
||||
}
|
||||
|
@ -233,7 +129,6 @@ fileprivate extension Reachability {
|
|||
try reachabilitySerialQueue.sync { [unowned self] in
|
||||
var flags = SCNetworkReachabilityFlags()
|
||||
if !SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags) {
|
||||
self.stopNotifier()
|
||||
throw ReachabilityError.unableToGetFlags(SCError())
|
||||
}
|
||||
|
||||
|
@ -307,9 +202,6 @@ extension SCNetworkReachabilityFlags {
|
|||
var isDirectFlagSet: Bool {
|
||||
return contains(.isDirect)
|
||||
}
|
||||
var isConnectionRequiredAndTransientFlagSet: Bool {
|
||||
return intersection([.connectionRequired, .transientConnection]) == [.connectionRequired, .transientConnection]
|
||||
}
|
||||
|
||||
var description: String {
|
||||
let W = isOnWWANFlagSet ? "W" : "-"
|
||||
|
@ -325,44 +217,3 @@ extension SCNetworkReachabilityFlags {
|
|||
return "\(W)\(R) \(c)\(t)\(i)\(C)\(D)\(l)\(d)"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
`ReachabilityWeakifier` weakly wraps the `Reachability` class
|
||||
in order to break retain cycles when interacting with CoreFoundation.
|
||||
|
||||
CoreFoundation callbacks expect a pair of retain/release whenever an
|
||||
opaque `info` parameter is provided. These callbacks exist to guard
|
||||
against memory management race conditions when invoking the callbacks.
|
||||
|
||||
#### Race Condition
|
||||
|
||||
If we passed `SCNetworkReachabilitySetCallback` a direct reference to our
|
||||
`Reachability` class without also providing corresponding retain/release
|
||||
callbacks, then a race condition can lead to crashes when:
|
||||
- `Reachability` is deallocated on thread X
|
||||
- A `SCNetworkReachability` callback(s) is already in flight on thread Y
|
||||
|
||||
#### Retain Cycle
|
||||
|
||||
If we pass `Reachability` to CoreFoundtion while also providing retain/
|
||||
release callbacks, we would create a retain cycle once CoreFoundation
|
||||
retains our `Reachability` class. This fixes the crashes and his how
|
||||
CoreFoundation expects the API to be used, but doesn't play nicely with
|
||||
Swift/ARC. This cycle would only be broken after manually calling
|
||||
`stopNotifier()` — `deinit` would never be called.
|
||||
|
||||
#### ReachabilityWeakifier
|
||||
|
||||
By providing both retain/release callbacks and wrapping `Reachability` in
|
||||
a weak wrapper, we:
|
||||
- interact correctly with CoreFoundation, thereby avoiding a crash.
|
||||
See "Memory Management Programming Guide for Core Foundation".
|
||||
- don't alter the public API of `Reachability.swift` in any way
|
||||
- still allow for automatic stopping of the notifier on `deinit`.
|
||||
*/
|
||||
private class ReachabilityWeakifier {
|
||||
weak var reachability: Reachability?
|
||||
init(reachability: Reachability) {
|
||||
self.reachability = reachability
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue