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)
}

Swift 3, IPv4, IPv6

Based on the Martin R's answer:

import SystemConfiguration

func isConnectedToNetwork() -> Bool {
    guard let flags = getFlags() else { return false }
    let isReachable = flags.contains(.reachable)
    let needsConnection = flags.contains(.connectionRequired)
    return (isReachable && !needsConnection)
}

func getFlags() -> SCNetworkReachabilityFlags? {
    guard let reachability = ipv4Reachability() ?? ipv6Reachability() else {
        return nil
    }
    var flags = SCNetworkReachabilityFlags()
    if !SCNetworkReachabilityGetFlags(reachability, &flags) {
        return nil
    }
    return flags
}

func ipv6Reachability() -> SCNetworkReachability? {
    var zeroAddress = sockaddr_in6()
    zeroAddress.sin6_len = UInt8(MemoryLayout<sockaddr_in>.size)
    zeroAddress.sin6_family = sa_family_t(AF_INET6)

    return withUnsafePointer(to: &zeroAddress, {
        $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
            SCNetworkReachabilityCreateWithAddress(nil, $0)
        }
    })
}

func ipv4Reachability() -> SCNetworkReachability? {
    var zeroAddress = sockaddr_in()
    zeroAddress.sin_len = UInt8(MemoryLayout<sockaddr_in>.size)
    zeroAddress.sin_family = sa_family_t(AF_INET)

    return withUnsafePointer(to: &zeroAddress, {
        $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
            SCNetworkReachabilityCreateWithAddress(nil, $0)
        }
    })
}

This has nothing to do with Swift, but the best solution is to NOT use Reachability to determine whether the network is online. Just make your connection and handle errors if it fails. Making a connection can at times fire up the dormant offline radios.

The one valid use of Reachability is to use it to notify you when a network transitions from offline to online. At that point you should retry failed connections.


Swift 5, Using NWPathMonitor

import Network

func configureNetworkMonitor(){
        let monitor = NWPathMonitor()
        
        monitor.pathUpdateHandler = { path in
            
            if path.status != .satisfied {
                print("not connected")
            }
            else if path.usesInterfaceType(.cellular) {
                print("Cellular")
            }
            else if path.usesInterfaceType(.wifi) {
                print("WIFI")
            }
            else if path.usesInterfaceType(.wiredEthernet) {
                print("Ethernet")
            }
            else if path.usesInterfaceType(.other){
                print("Other")
            }else if path.usesInterfaceType(.loopback){
                print("Loop Back")
            }
        }
        
        monitor.start(queue: DispatchQueue.global(qos: .background))
    }