HiveBrain v1.2.0
Get Started
← Back to all entries
patternswiftMinor

Creating SwiftReachability Class in Swift 2.2

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
creatingswiftclassswiftreachability

Problem

I am novice at creating independent classes for user. I have seen many 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 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)) != 0


SCNetworkReachabilityFlags 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 could
simply create a repeating NSTimer. Also you should check if the
startNotifier/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 displaying
alerts. 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 reference
a (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.