How to Use Scnetworkreachability in Swift

How to use SCNetworkReachability in Swift

(This answer was extended repeatedly due to changes in the Swift language, which made it a bit confusing. I have now rewritten it and removed everything which refers to Swift 1.x. The older code can
be found in the edit history if somebody needs it.)

This is how you would do it in Swift 2.0 (Xcode 7):

import SystemConfiguration

func connectedToNetwork() -> 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 {
return false
}

var flags : SCNetworkReachabilityFlags = []
if !SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) {
return false
}

let isReachable = flags.contains(.Reachable)
let needsConnection = flags.contains(.ConnectionRequired)

return (isReachable && !needsConnection)
}

Explanations:

  • As of Swift 1.2 (Xcode 6.3), imported C structs have a default initializer in Swift, which initializes all of the struct's fields to zero, so the socket address structure can be initialized with

    var zeroAddress = sockaddr_in()
  • sizeofValue() gives the size of this structure, this has
    to be converted to UInt8 for sin_len:

    zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress))
  • AF_INET is an Int32, this has to be converted to the correct type for sin_family:

    zeroAddress.sin_family = sa_family_t(AF_INET)
  • withUnsafePointer(&zeroAddress) { ... } passes the address of the
    structure to the closure where it is used as argument for
    SCNetworkReachabilityCreateWithAddress(). The UnsafePointer($0)
    conversion is needed because that function expects a pointer to
    sockaddr, not sockaddr_in.

  • The value returned from withUnsafePointer() is the return value
    from SCNetworkReachabilityCreateWithAddress() and that has the
    type SCNetworkReachability?, i.e. it is an optional.
    The guard let statement (a new feature in Swift 2.0) assigns the unwrapped value to the defaultRouteReachability variable if it is
    not nil. Otherwise the else block is executed and the function
    returns.

  • As of Swift 2, SCNetworkReachabilityCreateWithAddress() returns
    a managed object. You don't have to release it explicitly.
  • As of Swift 2, SCNetworkReachabilityFlags conforms to
    OptionSetType which has a set-like interface. You create an
    empty flags variable with

    var flags : SCNetworkReachabilityFlags = []

    and check for flags with

    let isReachable = flags.contains(.Reachable)
    let needsConnection = flags.contains(.ConnectionRequired)
  • The second parameter of SCNetworkReachabilityGetFlags has the type
    UnsafeMutablePointer<SCNetworkReachabilityFlags>, which means that you have to
    pass the address of the flags variable.

Note also that registering a notifier callback is possible as of
Swift 2, compare Working with C APIs from Swift and Swift 2 - UnsafeMutablePointer<Void> to object.


Update for Swift 3/4:

Unsafe pointers cannot be simply be converted to a pointer of a
different type anymore (see - SE-0107 UnsafeRawPointer API). Here the updated code:

import SystemConfiguration

func connectedToNetwork() -> Bool {

var zeroAddress = sockaddr_in()
zeroAddress.sin_len = UInt8(MemoryLayout<sockaddr_in>.size)
zeroAddress.sin_family = sa_family_t(AF_INET)

guard let defaultRouteReachability = withUnsafePointer(to: &zeroAddress, {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
SCNetworkReachabilityCreateWithAddress(nil, $0)
}
}) else {
return false
}

var flags: SCNetworkReachabilityFlags = []
if !SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) {
return false
}

let isReachable = flags.contains(.reachable)
let needsConnection = flags.contains(.connectionRequired)

return (isReachable && !needsConnection)
}

Detect if internet connection is available (WIFI and 4G) on SWIFT 3

I'm using a slightly different Reachability function. Try mine. It should work for you. I'm using it in almost every of my apps and I'm not having any kind of problems with it.

Written in Swift3

import Foundation
import SystemConfiguration

open class Reachability {

class func isConnectedToNetwork() -> Bool {

var zeroAddress = sockaddr_in()
zeroAddress.sin_len = UInt8(MemoryLayout.size(ofValue: zeroAddress))
zeroAddress.sin_family = sa_family_t(AF_INET)

guard let defaultRouteReachability = withUnsafePointer(to: &zeroAddress , {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {zeroSockAddress in
SCNetworkReachabilityCreateWithAddress(nil, zeroSockAddress)
}
}) else {
return false
}

var flags : SCNetworkReachabilityFlags = []
if !SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) {
return false
}

let isReachable = flags.contains(.reachable)
let needsConnection = flags.contains(.connectionRequired)
return (isReachable && !needsConnection)
}

}

Called by:

if Reachability.isConnectedToNetwork() == true {
xy
} else {
yz
}

Check For Internet Connectivity In Swift

The easiest way to check for internet connectivity is to use the iOS reachability api.

It's available in the SystemConfiguration module.

Objective-C Reachability Example

A good example of using SCNetworkReachability in swift is found in the stack overflow answer:

SCNetworkReachability in swift

I hope this helps! Have a nice day!

SCNetworkReachabilityCallBack? type conversion issue with Swift3.0

If you have something odd in Swift 3, always check the latest reference:
(As for now, the latest reference is up to the latest Xcode 8, beta 6. If you are using beta 5 or older, the code below does not work.)

Declaration

typealias SCNetworkReachabilityCallBack = (
SCNetworkReachability,
SCNetworkReachabilityFlags,
UnsafeMutableRawPointer?) -> Void

The type of the last parameter of the call back has changed to UnsafeMutableRawPointer?.

So, you may need to change your callback to something like this:

func callback(_ reachability:SCNetworkReachability, flags: SCNetworkReachabilityFlags, info: UnsafeMutableRawPointer?) {
let reachability = Unmanaged<Reachability>.fromOpaque(info!).takeUnretainedValue()

DispatchQueue.main.async {
reachability.reachabilityChanged(flags)
}
}

Using Apple's Reachability to check remote server reachability in Swift

The Reachability class you're using is based on Apple's SCNetworkReachability class, which doesn't do exactly what you're hoping. From the SCNetworkReachability documentation:

A remote host is considered reachable when a data packet, sent by an
application into the network stack, can leave the local device.
Reachability does not guarantee that the data packet will actually be
received by the host.

So it's not built for testing whether or not the remote host is actually online, just whether (1) the current network settings will allow an attempt to reach it and (2) by what methods. Once you've determined that the network is active you'll need to make an attempt to connect to see if the remote host is actually up and running.


Note: This test:

(ReachabilityInst.currentReachabilityStatus().value == ReachableViaWiFi.value)

is the correct way to check -- for some reason NetworkStatus is one of the few Apple enumerations created without the NS_ENUM macro.

Working with C APIs from Swift

Your first problem can be solved with

let reachability = host.withCString {
SCNetworkReachabilityCreateWithName(nil, $0).takeRetainedValue()
}

Inside the closure, $0 is a pointer to the NUL-terminated UTF-8 representation of the
String.

Update: As Nate Cook said in
a now deleted answer and also here,
you can actually pass a Swift string to a function taking a UnsafePointer<UInt8>
directly:

let reachability = SCNetworkReachabilityCreateWithName(nil, host).takeRetainedValue()

Unfortunately there is (as far as I know) currently no solution to your second problem.
SCNetworkReachabilitySetCallback expects a pointer to a C function as second
parameter, and there is currently no method to pass a Swift function or closure.
Note that the documentation for SCNetworkReachabilityCallBack
shows only Objective-C but no Swift.

See also Does Swift not work with function pointers?.


Update for Swift 2: It is now possible to pass a Swift closure
to a C function taking a function pointer parameter. Also
SCNetworkReachabilityCreateWithName()
does not return an unmanaged object anymore:

let host = "google.com"
var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
let reachability = SCNetworkReachabilityCreateWithName(nil, host)!

SCNetworkReachabilitySetCallback(reachability, { (_, flags, _) in
print(flags)
}, &context)

SCNetworkReachabilityScheduleWithRunLoop(reachability, CFRunLoopGetMain(), kCFRunLoopCommonModes)

Update for Swift 3 (Xcode 8):

let host = "google.com"
var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
let reachability = SCNetworkReachabilityCreateWithName(nil, host)!

SCNetworkReachabilitySetCallback(reachability, { (_, flags, _) in
print(flags)
}, &context)

SCNetworkReachabilityScheduleWithRunLoop(reachability, CFRunLoopGetMain(),
CFRunLoopMode.commonModes.rawValue)


Related Topics



Leave a reply



Submit