patternswiftMinor
Creating SwiftReachability Class in Swift 2.2
Viewed 0 times
creatingswiftclassswiftreachability
Problem
I am novice at creating independent classes for user. I have seen many
```
class SwiftReachability: NSObject {
let REACHABILITY_NOTIFIER_KEY = "reachability_notifier_key"
var backgroundQueue: NSOperationQueue?
var isCancelled: Bool = false
//MARK: - Checking Internet Connecion
func isConnectedToNetwork() -> Bool{
var zeroAddress = sockaddr_in(sin_len: 0, sin_family: 0, sin_port: 0, sin_addr: in_addr(s_addr: 0), sin_zero: (0, 0, 0, 0, 0, 0, 0, 0))
zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress))
zeroAddress.sin_family = sa_family_t(AF_INET)
let defaultRouteReachability = withUnsafePointer(&zeroAddress) {
SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0))
}
var flags = SCNetworkReachabilityFlags(rawValue: 0)
SCNetworkReachabilityGetFlags(defaultRouteReachability!, &flags)
#if DEBUG
print("Reachability flags = \(flags.rawValue) uint = \(UInt32(kSCNetworkFlagsReachable))")
#endif
let isReachable = (flags.rawValue & UInt32(kSCNetworkFlagsReachable)) != 0
let needsConnection = (flags.rawValue & UInt32(kSCNetworkFlagsConnectionRequired)) != 0
return (isReachable && !needsConnection) ? true : false
}
func startNotifier(){
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(SwiftReachability.networkConnectionObserver), name: REACHABILITY_NOTIFIER_KEY, object: nil)
callingBackgroungThread()
}
func stopNotifier(){
NSNotificationCenter.defaultCenter().removeObserver(self, name: REACHABILITY_NOTIFIER_KEY, object: nil)
backgroundQueue?.cancelAllOperations()
isCancelled = true
}
func callingBackgroungThread(){
backgroundQueue = NSOperationQueue()
let operation = NSBlockOperation {
if self.isConnectedToNetwork(){
NSNotificationCenter.defaultCenter().postNotificationName(self.REACHABILITY_NOTIFIER_KEY, object:nil)
Reachability library for Swift, but I need to create my own class for that. Here is my code:```
class SwiftReachability: NSObject {
let REACHABILITY_NOTIFIER_KEY = "reachability_notifier_key"
var backgroundQueue: NSOperationQueue?
var isCancelled: Bool = false
//MARK: - Checking Internet Connecion
func isConnectedToNetwork() -> Bool{
var zeroAddress = sockaddr_in(sin_len: 0, sin_family: 0, sin_port: 0, sin_addr: in_addr(s_addr: 0), sin_zero: (0, 0, 0, 0, 0, 0, 0, 0))
zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress))
zeroAddress.sin_family = sa_family_t(AF_INET)
let defaultRouteReachability = withUnsafePointer(&zeroAddress) {
SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0))
}
var flags = SCNetworkReachabilityFlags(rawValue: 0)
SCNetworkReachabilityGetFlags(defaultRouteReachability!, &flags)
#if DEBUG
print("Reachability flags = \(flags.rawValue) uint = \(UInt32(kSCNetworkFlagsReachable))")
#endif
let isReachable = (flags.rawValue & UInt32(kSCNetworkFlagsReachable)) != 0
let needsConnection = (flags.rawValue & UInt32(kSCNetworkFlagsConnectionRequired)) != 0
return (isReachable && !needsConnection) ? true : false
}
func startNotifier(){
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(SwiftReachability.networkConnectionObserver), name: REACHABILITY_NOTIFIER_KEY, object: nil)
callingBackgroungThread()
}
func stopNotifier(){
NSNotificationCenter.defaultCenter().removeObserver(self, name: REACHABILITY_NOTIFIER_KEY, object: nil)
backgroundQueue?.cancelAllOperations()
isCancelled = true
}
func callingBackgroungThread(){
backgroundQueue = NSOperationQueue()
let operation = NSBlockOperation {
if self.isConnectedToNetwork(){
NSNotificationCenter.defaultCenter().postNotificationName(self.REACHABILITY_NOTIFIER_KEY, object:nil)
Solution
Improvements of your code
Let's start with your
As of Swift 1.2(?), all imported C structs have a default initializer which sets all
members to zero:
Here
Better check with
This can be shorted to
there is no need for
Finally, there is no reason to make this an instance method because it does not use
any state, you could make it a type method instead. Then it would look like this:
Your notification mechanism is far too complicated in my opinion. Instead of
scheduling an
simply create a repeating
Even better, you can register a notification callback which is automatically called
when the reachability status changes. I'll come back to that later.
The
alerts. Use
A different design
Let's have a look at the "Supporting IPv6 DNS64/NAT64 Networks"
document from Apple:
Connect Without Preflight
The Reachability APIs (see SCNetworkReachability Reference) are intended for diagnostic
purposes after identifying a connectivity issue. Many apps incorrectly use these APIs
to proactively check for an Internet connection by calling the SCNetworkReachabilityCreateWithAddress
method and passing it an IPv4 address of 0.0.0.0, which indicates that there is a
router on the network. However, the presence of a router doesn’t guarantee that an
Internet connection exists. In general, avoid preflighting network reachability.
Just try to make a connection and gracefully handle failures. If you must check for
network availability, avoid calling the SCNetworkReachabilityCreateWithAddress method.
Call the SCNetworkReachabilityCreateWithName method and pass it a hostname instead.
Your method can only test for general Internet connectivity (reachability of 0.0.0.0),
but not for reachability of a given host (as recommended by Apple).
To add this feature, it makes sense to make the
a (private) instance variable, and provide multiple
More init methods (e.g. for `SCNetworkReachabilityCreateWit
Let's start with your
isConnectedToNetwork() method.var zeroAddress = sockaddr_in(sin_len: 0, sin_family: 0, sin_port: 0, sin_addr: in_addr(s_addr: 0), sin_zero: (0, 0, 0, 0, 0, 0, 0, 0))As of Swift 1.2(?), all imported C structs have a default initializer which sets all
members to zero:
var zeroAddress = sockaddr_in()let defaultRouteReachability = withUnsafePointer(&zeroAddress) {
SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0))
}Here
defaultRouteReachability is an optional which is later force-unwrapped.Better check with
guard and optional binding if the call succeeded:guard let defaultRouteReachability = withUnsafePointer(&zeroAddress, {
SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0))
}) else {
NSLog("Could not create reachability reference")
return false
}var flags = SCNetworkReachabilityFlags(rawValue: 0)This can be shorted to
var flags = SCNetworkReachabilityFlags()
// or
var flags : SCNetworkReachabilityFlags = []let isReachable = (flags.rawValue & UInt32(kSCNetworkFlagsReachable)) != 0
let needsConnection = (flags.rawValue & UInt32(kSCNetworkFlagsConnectionRequired)) != 0SCNetworkReachabilityFlags is an OptionSetType and that has a set-like interface,there is no need for
rawValue and UInt32 conversion:let isReachable = flags.contains(.Reachable)
let needsConnection = flags.contains(.ConnectionRequired)Finally, there is no reason to make this an instance method because it does not use
any state, you could make it a type method instead. Then it would look like this:
class func isConnectedToNetwork() -> Bool{
var zeroAddress = sockaddr_in()
zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress))
zeroAddress.sin_family = sa_family_t(AF_INET)
guard let defaultRouteReachability = withUnsafePointer(&zeroAddress, {
SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0))
}) else {
NSLog("Could not create reachability reference")
return false
}
var flags : SCNetworkReachabilityFlags = []
SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags)
let isReachable = flags.contains(.Reachable)
let needsConnection = flags.contains(.ConnectionRequired)
return isReachable && !needsConnection
}Your notification mechanism is far too complicated in my opinion. Instead of
scheduling an
NSOperation which reschedules itself with dispatch_after, you couldsimply create a repeating
NSTimer. Also you should check if thestartNotifier/stopNotifier calls are properly balanced.Even better, you can register a notification callback which is automatically called
when the reachability status changes. I'll come back to that later.
The
SwiftReachability class should not do any UI-related stuff like displayingalerts. Use
NSNotification or callback methods to make the class reusable.A different design
Let's have a look at the "Supporting IPv6 DNS64/NAT64 Networks"
document from Apple:
Connect Without Preflight
The Reachability APIs (see SCNetworkReachability Reference) are intended for diagnostic
purposes after identifying a connectivity issue. Many apps incorrectly use these APIs
to proactively check for an Internet connection by calling the SCNetworkReachabilityCreateWithAddress
method and passing it an IPv4 address of 0.0.0.0, which indicates that there is a
router on the network. However, the presence of a router doesn’t guarantee that an
Internet connection exists. In general, avoid preflighting network reachability.
Just try to make a connection and gracefully handle failures. If you must check for
network availability, avoid calling the SCNetworkReachabilityCreateWithAddress method.
Call the SCNetworkReachabilityCreateWithName method and pass it a hostname instead.
Your method can only test for general Internet connectivity (reachability of 0.0.0.0),
but not for reachability of a given host (as recommended by Apple).
To add this feature, it makes sense to make the
SCNetworkReachability referencea (private) instance variable, and provide multiple
init methods:class SwiftReachability {
private let reachability: SCNetworkReachability
init?() {
var zeroAddress = sockaddr_in()
zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress))
zeroAddress.sin_family = sa_family_t(AF_INET)
guard let reachability = withUnsafePointer(&zeroAddress, {
SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0))
}) else {
return nil
}
self.reachability = reachability
}
init?(hostname : String) {
guard let reachability = SCNetworkReachabilityCreateWithName(nil, hostname) else {
return nil
}
self.reachability = reachability
}
}More init methods (e.g. for `SCNetworkReachabilityCreateWit
Code Snippets
var zeroAddress = sockaddr_in(sin_len: 0, sin_family: 0, sin_port: 0, sin_addr: in_addr(s_addr: 0), sin_zero: (0, 0, 0, 0, 0, 0, 0, 0))var zeroAddress = sockaddr_in()let defaultRouteReachability = withUnsafePointer(&zeroAddress) {
SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0))
}guard let defaultRouteReachability = withUnsafePointer(&zeroAddress, {
SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0))
}) else {
NSLog("Could not create reachability reference")
return false
}var flags = SCNetworkReachabilityFlags(rawValue: 0)Context
StackExchange Code Review Q#127451, answer score: 4
Revisions (0)
No revisions yet.