patternswiftMinor
Checking network status
Viewed 0 times
checkingnetworkstatus
Problem
I have this slightly popular Swift library called IJReachability up on Github. It checks the network connection status. Due to my office workload I've been very busy in the past few months so I couldn't attend to the project and keep in touch with the folks who are contributing.
Now I'm back and hoping to dust off the project, update to Swift 2 (probably still keep the 1.2 version in a separate branch) and activly participate on developing it.
The project was originally written in Swift 1.2:
```
import Foundation
import SystemConfiguration
public enum IJReachabilityType {
case WWAN,
WiFi,
NotConnected
}
public class IJReachability {
public class 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)).takeRetainedValue()
}
var flags: SCNetworkReachabilityFlags = 0
if SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) == 0 {
return false
}
let isReachable = (flags & UInt32(kSCNetworkFlagsReachable)) != 0
let needsConnection = (flags & UInt32(kSCNetworkFlagsConnectionRequired)) != 0
return (isReachable && !needsConnection) ? true : false
}
public class func isConnectedToNetworkOfType() -> IJReachabilityType {
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) {
SCNetworkReachabilityC
Now I'm back and hoping to dust off the project, update to Swift 2 (probably still keep the 1.2 version in a separate branch) and activly participate on developing it.
The project was originally written in Swift 1.2:
```
import Foundation
import SystemConfiguration
public enum IJReachabilityType {
case WWAN,
WiFi,
NotConnected
}
public class IJReachability {
public class 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)).takeRetainedValue()
}
var flags: SCNetworkReachabilityFlags = 0
if SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) == 0 {
return false
}
let isReachable = (flags & UInt32(kSCNetworkFlagsReachable)) != 0
let needsConnection = (flags & UInt32(kSCNetworkFlagsConnectionRequired)) != 0
return (isReachable && !needsConnection) ? true : false
}
public class func isConnectedToNetworkOfType() -> IJReachabilityType {
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) {
SCNetworkReachabilityC
Solution
Using a tuple as return type is generally fine. I haven't seen it much
either in Apple's frameworks, the only one that I know of at present
is the
In your particular case however, I think there is a better way.
The reachability type is only relevant if the status is
combinations make sense.
I would therefore include the type into the status by
using enums with associated values:
Now your
possible values are:
and
The
that if the status could not be determined (e.g. retrieving the
flags failed).
The code duplication for evaluating the flags can be solved by
defining an (perhaps private) init method of
takes an
so that your function would now look like this:
More remarks: Instead of using the
you could pass a closure as a callback to the
the hassle to wrap (and later unwrap) the status into some Objective-C
compatible type. (Added: However, that would require more changes
because a C callback cannot capture context. See Swift 2 - UnsafeMutablePointer to object for a possible solution.)
Note also that you need some method to stop the monitoring, i.e.
to unregister the reachability from the run loop.
All information get be retrieved from this single
value with the associated
for all possible values with a switch statement:
or use if/case with pattern matching to check for a certain state:
either in Apple's frameworks, the only one that I know of at present
is the
String methodpublic static func fromCStringRepairingIllFormedUTF8(cs: UnsafePointer) -> (String?, hadError: Bool)In your particular case however, I think there is a better way.
The reachability type is only relevant if the status is
.Online, and not all type/statuscombinations make sense.
I would therefore include the type into the status by
using enums with associated values:
enum ReachabilityType: CustomStringConvertible {
case WWAN
case WiFi
// No case NotConnected anymore.
var description: String {
switch self {
case .WWAN: return "WWAN"
case .WiFi: return "WiFi"
}
}
}
enum ReachabilityStatus: CustomStringConvertible {
case Offline
case Online(ReachabilityType) // Type as an associated value.
case Unknown
var description: String {
switch self {
case .Offline: return "Offline"
case .Online(let type): return "Online(\(type))"
case .Unknown: return "Unknown"
}
}
}Now your
connectedToNetwork() function should return a ReachabilityStatus, andpossible values are:
.Unknown, .Offline, .Online(.WWAN)and
.Online(.WiFi). A better name for that function might beconnectionStatus().The
.Unknown status is currently not used at all. You might returnthat if the status could not be determined (e.g. retrieving the
flags failed).
The code duplication for evaluating the flags can be solved by
defining an (perhaps private) init method of
ReachabilityStatus whichtakes an
SCNetworkReachabilityFlags parameter:extension ReachabilityStatus {
private init(reachabilityFlags flags: SCNetworkReachabilityFlags) {
let connectionRequired = flags.contains(.ConnectionRequired)
let isReachable = flags.contains(.Reachable)
let isWWAN = flags.contains(.IsWWAN)
if !connectionRequired && isReachable {
if isWWAN {
self = .Online(.WWAN)
} else {
self = .Online(.WiFi)
}
} else {
self = .Offline
}
}
}so that your function would now look like this:
func connectionStatus() -> ReachabilityStatus {
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 {
return .Unknown
}
var flags : SCNetworkReachabilityFlags = []
if !SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) {
return .Unknown
}
return ReachabilityStatus(reachabilityFlags: flags)
}More remarks: Instead of using the
NSNotificationCenteryou could pass a closure as a callback to the
monitorReachabilityChanges() function. That would save you from the hassle to wrap (and later unwrap) the status into some Objective-C
compatible type. (Added: However, that would require more changes
because a C callback cannot capture context. See Swift 2 - UnsafeMutablePointer to object for a possible solution.)
Note also that you need some method to stop the monitoring, i.e.
to unregister the reachability from the run loop.
All information get be retrieved from this single
ReachabilityStatus value with the associated
ReachabilityType. You can checkfor all possible values with a switch statement:
switch status {
case .Unknown, .Offline:
print("not connected")
case .Online(.WWAN):
print("connected via WWan")
case .Online(.WiFi):
print("connected via WiFi")
}or use if/case with pattern matching to check for a certain state:
if case .Online = status {
print("online")
} else {
print("offline")
}
if case .Online(.WiFi) = status {
print("connected via WiFi")
}Code Snippets
public static func fromCStringRepairingIllFormedUTF8(cs: UnsafePointer<CChar>) -> (String?, hadError: Bool)enum ReachabilityType: CustomStringConvertible {
case WWAN
case WiFi
// No case NotConnected anymore.
var description: String {
switch self {
case .WWAN: return "WWAN"
case .WiFi: return "WiFi"
}
}
}
enum ReachabilityStatus: CustomStringConvertible {
case Offline
case Online(ReachabilityType) // Type as an associated value.
case Unknown
var description: String {
switch self {
case .Offline: return "Offline"
case .Online(let type): return "Online(\(type))"
case .Unknown: return "Unknown"
}
}
}extension ReachabilityStatus {
private init(reachabilityFlags flags: SCNetworkReachabilityFlags) {
let connectionRequired = flags.contains(.ConnectionRequired)
let isReachable = flags.contains(.Reachable)
let isWWAN = flags.contains(.IsWWAN)
if !connectionRequired && isReachable {
if isWWAN {
self = .Online(.WWAN)
} else {
self = .Online(.WiFi)
}
} else {
self = .Offline
}
}
}func connectionStatus() -> ReachabilityStatus {
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 {
return .Unknown
}
var flags : SCNetworkReachabilityFlags = []
if !SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) {
return .Unknown
}
return ReachabilityStatus(reachabilityFlags: flags)
}switch status {
case .Unknown, .Offline:
print("not connected")
case .Online(.WWAN):
print("connected via WWan")
case .Online(.WiFi):
print("connected via WiFi")
}Context
StackExchange Code Review Q#107129, answer score: 3
Revisions (0)
No revisions yet.