273

When I try to check for an internet connection on my iPhone I get a bunch of errors. Can anyone help me to fix this?

The code:

import Foundation
import SystemConfiguration

public class Reachability {

class func isConnectedToNetwork() -> Bool {

    var zeroAddress = sockaddr_in()
    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 = 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
}

}

The errors with the code:

Errors

If it is unreadable, error 1 says:

'Int' is not convertible to 'SCNetworkReachabilityFlags'

Error 2 & 3:

Could not find an overload for 'init' that accepts the supplied arguments

Eric Aya
  • 68,765
  • 33
  • 165
  • 232
b3rge
  • 4,529
  • 7
  • 20
  • 23
  • 1
    The code looks similar to what I had suggested here: http://stackoverflow.com/a/25623647/1187415, so I have updated that one for Swift 2. – Martin R Jun 10 '15 at 01:32
  • 2
    I've got a fully working Swift 2 Reachability implementation here… http://github.com/ashleymills/Reachability.swift - Feel free to use that as a reference. – Ashley Mills Sep 19 '15 at 11:59
  • 1
    http://stackoverflow.com/a/25623647/1187415 Is now updated for Swift 3, Xcode 8 beta 6. – Martin R Aug 19 '16 at 22:55
  • 2
    [Apple provides an official Swift sample](https://developer.apple.com/library/ios/samplecode/Reachability/), and you can [get it on CocoaPods](https://cocoapods.org/pods/AppleReachability). It is compatible with any Swift project. – Cœur Mar 15 '17 at 13:52
  • https://stackoverflow.com/a/42710600/6898523 Or https://stackoverflow.com/a/43801932/6898523 – MAhipal Singh Jul 20 '18 at 07:12
  • Check this out I've answered here https://stackoverflow.com/a/63452668/10549967 – mirhpe danielle Aug 17 '20 at 14:13

26 Answers26

387

To solve the 4G issue mentioned in the comments I have used @AshleyMills reachability implementation as a reference and rewritten Reachability for Swift 3.1:

updated: Xcode 10.1 • Swift 4 or later


Reachability.swift file

import Foundation
import SystemConfiguration

class Reachability {
    var hostname: String?
    var isRunning = false
    var isReachableOnWWAN: Bool
    var reachability: SCNetworkReachability?
    var reachabilityFlags = SCNetworkReachabilityFlags()
    let reachabilitySerialQueue = DispatchQueue(label: "ReachabilityQueue")
    init(hostname: String) throws {
        guard let reachability = SCNetworkReachabilityCreateWithName(nil, hostname) else {
            throw Network.Error.failedToCreateWith(hostname)
        }
        self.reachability = reachability
        self.hostname = hostname
        isReachableOnWWAN = true
        try start()
    }
    init() throws {
        var zeroAddress = sockaddr_in()
        zeroAddress.sin_len = UInt8(MemoryLayout<sockaddr_in>.size)
        zeroAddress.sin_family = sa_family_t(AF_INET)
        guard let reachability = withUnsafePointer(to: &zeroAddress, {
            $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
                SCNetworkReachabilityCreateWithAddress(nil, $0)
            }
        }) else {
            throw Network.Error.failedToInitializeWith(zeroAddress)
        }
        self.reachability = reachability
        isReachableOnWWAN = true
        try start()
    }
    var status: Network.Status {
        return  !isConnectedToNetwork ? .unreachable :
                isReachableViaWiFi    ? .wifi :
                isRunningOnDevice     ? .wwan : .unreachable
    }
    var isRunningOnDevice: Bool = {
        #if targetEnvironment(simulator)
            return false
        #else
            return true
        #endif
    }()
    deinit { stop() }
}

extension Reachability {

    func start() throws {
        guard let reachability = reachability, !isRunning else { return }
        var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
        context.info = Unmanaged<Reachability>.passUnretained(self).toOpaque()
        guard SCNetworkReachabilitySetCallback(reachability, callout, &context) else { stop()
            throw Network.Error.failedToSetCallout
        }
        guard SCNetworkReachabilitySetDispatchQueue(reachability, reachabilitySerialQueue) else { stop()
            throw Network.Error.failedToSetDispatchQueue
        }
        reachabilitySerialQueue.async { self.flagsChanged() }
        isRunning = true
    }

    func stop() {
        defer { isRunning = false }
        guard let reachability = reachability else { return }
        SCNetworkReachabilitySetCallback(reachability, nil, nil)
        SCNetworkReachabilitySetDispatchQueue(reachability, nil)
        self.reachability = nil
    }

    var isConnectedToNetwork: Bool {
        return isReachable &&
               !isConnectionRequiredAndTransientConnection &&
               !(isRunningOnDevice && isWWAN && !isReachableOnWWAN)
    }

    var isReachableViaWiFi: Bool {
        return isReachable && isRunningOnDevice && !isWWAN
    }

    /// Flags that indicate the reachability of a network node name or address, including whether a connection is required, and whether some user intervention might be required when establishing a connection.
    var flags: SCNetworkReachabilityFlags? {
        guard let reachability = reachability else { return nil }
        var flags = SCNetworkReachabilityFlags()
        return withUnsafeMutablePointer(to: &flags) {
            SCNetworkReachabilityGetFlags(reachability, UnsafeMutablePointer($0))
            } ? flags : nil
    }

    /// compares the current flags with the previous flags and if changed posts a flagsChanged notification
    func flagsChanged() {
        guard let flags = flags, flags != reachabilityFlags else { return }
        reachabilityFlags = flags
        NotificationCenter.default.post(name: .flagsChanged, object: self)
    }

    /// The specified node name or address can be reached via a transient connection, such as PPP.
    var transientConnection: Bool { return flags?.contains(.transientConnection) == true }

    /// The specified node name or address can be reached using the current network configuration.
    var isReachable: Bool { return flags?.contains(.reachable) == true }

    /// The specified node name or address can be reached using the current network configuration, but a connection must first be established. If this flag is set, the kSCNetworkReachabilityFlagsConnectionOnTraffic flag, kSCNetworkReachabilityFlagsConnectionOnDemand flag, or kSCNetworkReachabilityFlagsIsWWAN flag is also typically set to indicate the type of connection required. If the user must manually make the connection, the kSCNetworkReachabilityFlagsInterventionRequired flag is also set.
    var connectionRequired: Bool { return flags?.contains(.connectionRequired) == true }

    /// The specified node name or address can be reached using the current network configuration, but a connection must first be established. Any traffic directed to the specified name or address will initiate the connection.
    var connectionOnTraffic: Bool { return flags?.contains(.connectionOnTraffic) == true }

    /// The specified node name or address can be reached using the current network configuration, but a connection must first be established.
    var interventionRequired: Bool { return flags?.contains(.interventionRequired) == true }

    /// The specified node name or address can be reached using the current network configuration, but a connection must first be established. The connection will be established "On Demand" by the CFSocketStream programming interface (see CFStream Socket Additions for information on this). Other functions will not establish the connection.
    var connectionOnDemand: Bool { return flags?.contains(.connectionOnDemand) == true }

    /// The specified node name or address is one that is associated with a network interface on the current system.
    var isLocalAddress: Bool { return flags?.contains(.isLocalAddress) == true }

    /// Network traffic to the specified node name or address will not go through a gateway, but is routed directly to one of the interfaces in the system.
    var isDirect: Bool { return flags?.contains(.isDirect) == true }

    /// The specified node name or address can be reached via a cellular connection, such as EDGE or GPRS.
    var isWWAN: Bool { return flags?.contains(.isWWAN) == true }

    /// The specified node name or address can be reached using the current network configuration, but a connection must first be established. If this flag is set
    /// The specified node name or address can be reached via a transient connection, such as PPP.
    var isConnectionRequiredAndTransientConnection: Bool {
        return (flags?.intersection([.connectionRequired, .transientConnection]) == [.connectionRequired, .transientConnection]) == true
    }
}

func callout(reachability: SCNetworkReachability, flags: SCNetworkReachabilityFlags, info: UnsafeMutableRawPointer?) {
    guard let info = info else { return }
    DispatchQueue.main.async {
        Unmanaged<Reachability>
            .fromOpaque(info)
            .takeUnretainedValue()
            .flagsChanged()
    }
}

extension Notification.Name {
    static let flagsChanged = Notification.Name("FlagsChanged")
}

struct Network {
    static var reachability: Reachability!
    enum Status: String {
        case unreachable, wifi, wwan
    }
    enum Error: Swift.Error {
        case failedToSetCallout
        case failedToSetDispatchQueue
        case failedToCreateWith(String)
        case failedToInitializeWith(sockaddr_in)
    }
}

Usage

Initialize it in your AppDelegate.swift didFinishLaunchingWithOptions method and handle any errors that might occur:

import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        do {
            try Network.reachability = Reachability(hostname: "www.google.com")
        }
        catch {
            switch error as? Network.Error {
            case let .failedToCreateWith(hostname)?:
                print("Network error:\nFailed to create reachability object With host named:", hostname)
            case let .failedToInitializeWith(address)?:
                print("Network error:\nFailed to initialize reachability object With address:", address)
            case .failedToSetCallout?:
                print("Network error:\nFailed to set callout")
            case .failedToSetDispatchQueue?:
                print("Network error:\nFailed to set DispatchQueue")
            case .none:
                print(error)
            }
        }
        return true
    }
}

And a view controller sample:

import UIKit
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        NotificationCenter.default
            .addObserver(self,
                         selector: #selector(statusManager),
                         name: .flagsChanged,
                         object: nil)
        updateUserInterface()
    }
    func updateUserInterface() {
        switch Network.reachability.status {
        case .unreachable:
            view.backgroundColor = .red
        case .wwan:
            view.backgroundColor = .yellow
        case .wifi:
            view.backgroundColor = .green
        }
        print("Reachability Summary")
        print("Status:", Network.reachability.status)
        print("HostName:", Network.reachability.hostname ?? "nil")
        print("Reachable:", Network.reachability.isReachable)
        print("Wifi:", Network.reachability.isReachableViaWiFi)
    }
    @objc func statusManager(_ notification: Notification) {
        updateUserInterface()
    }
}

Sample Project

Leo Dabus
  • 198,248
  • 51
  • 423
  • 494
  • I hope that you do not mind, but the code from the question looked similar to what I had once suggested here: http://stackoverflow.com/a/25623647/1187415, so I have updated that answer to Swift 2 as well. – There are small differences though, note that instead of `UInt32(flags.rawValue)` one can use the new set-like interface of `OptionSetType`. – Martin R Jun 10 '15 at 01:40
  • flags.rawValue returns UInt32 so UInt32 is not needed also – Leo Dabus Jun 10 '15 at 02:25
  • What I meant is `let isReachable = flags.contains(.Reachable)` as in http://stackoverflow.com/a/25623647/1187415 :) – Martin R Jun 10 '15 at 02:39
  • 2
    You need to remove ` == 0` from the `if SCNetworkReachabilityGetFlags(…)` as xCode returns a Bool in it latest version. – Melvin Sep 11 '15 at 02:42
  • this code always returns as false even though i have active internet connection – Ramesh Lingappa Sep 12 '15 at 07:27
  • 75
    This is **NOT** correct as @NickM mentioned. **WILL NOT WORK FOR CELLULAR CONNECTIONS**. Don't use this. I'm surprised it has so many up votes, maybe this is new as of iOS 9... but I don't think so. – Jordan Smith Mar 03 '16 at 03:33
  • 1
    Doesn't work on IOS9, found out the very hard way - app in store... so I guess even if it's not 100% accurate and it works for some, it's inconsistent at best, so whoever wants to take chances might also want to bet on some horse or doggie if they're feeling lucky. – Nick M Mar 03 '16 at 10:36
  • 2
    @NickM - that explains why so many up votes. @ Leo it might be a good idea to update your answer for iOS 9, to save other people stumbling into the same trap. I can confirm it doesn't work (at least in some situations) on iOS 9... changing to github.com/ashleymills/Reachability.swift works fine however. – Jordan Smith Mar 03 '16 at 11:27
  • This also doesn't seem to work - I'm running on an iPhone 5 debugging with a USB cable and connected via 3G and isReachable comes back as false, even though I successfully use the 3G connection in other apps. – alpsystems.com Mar 04 '16 at 00:57
  • Swift 3 error: " 'init' is unavailable: use 'withMemoryRebound(to:capacity:_)' to temporarily view memory as another layout-compatible type." UnsafePointer has been renamed to UnsafeRawPointer as per https://github.com/apple/swift-evolution/blob/master/proposals/0055-optional-unsafe-pointers.md – Ace Green Aug 18 '16 at 17:20
  • @LeoDabus I have seen that library, is that the recommended way going forward? If so, maybe edit your reply to include it – Ace Green Aug 18 '16 at 17:33
  • @LeoDabus doesn't look like your version is updated to beta 6?? – Ace Green Aug 18 '16 at 17:47
  • No rush thanks for the effort. I updated yours as far as I could.. still some issues though https://dl.dropboxusercontent.com/u/8061535/Reachability.swift.zip – Ace Green Aug 18 '16 at 17:51
  • I did, hence the changes. What do you think I did? Change yours then complained it didn't work? :P – Ace Green Aug 18 '16 at 17:55
  • I'm not sure what you mean by that. But the Reachability.swift in the link you sent me doesnt work on beta 6. My changes are the Xcode suggested improvements, with 4 errors remaining. Like the use of UnsafePointer($0) which was the error I got with my own code when I first reached out here – Ace Green Aug 18 '16 at 17:58
  • Oh so you took out anyone function referring UnsafePointer which yes fixes the error in beta 6 but takes away necessary functionality. Unfortunately I need reachabilityForInternetConnection() – Ace Green Aug 19 '16 at 00:52
  • @AceGreen you don't need that and that was taken out by the author. Just use `reachability = try Reachability()` without any host – Leo Dabus Aug 19 '16 at 00:55
  • 1
    Yea I see it was taking out in the iOS10 branch – Ace Green Aug 19 '16 at 01:00
  • this code doesnt check the internet connectivity if connected to wifi. – vaibhav Dec 27 '16 at 12:43
  • @LeoDabus what i mean to say is the above code you have posted is only checking whether the wifi is connected to device whereas it doesn't checks the internet connectivity ultimately. Working with swift2 is not a issue :) – vaibhav Dec 27 '16 at 14:52
  • 1
    I think `#if (arch(i386) || arch(x86_64)) && os(iOS)` should return true? – Timeless May 03 '17 at 08:37
  • Seems like its not really working on the ios 10 for 4G connection. – Chinthaka Devinda May 05 '17 at 06:35
  • @ChinthakaDevinda What do you mean ? There is a sample project that was fully tested in the bottom of the answer. – Leo Dabus May 05 '17 at 10:50
  • if (Network.reachability?.isConnectedToNetwork)! {print("Call API")} – Shuvo Joseph Aug 14 '18 at 10:26
  • 1
    @LeoDabus Thanks for the code. Most user might not need to use the NotificationCenter and observe the status! They might just need to check if internet is available like me and then call api. – Shuvo Joseph Aug 14 '18 at 11:59
  • where should I put `func callout(reachability: SCNetworkReachability, flags: SCNetworkReachabilityFlags, info: UnsafeMutableRawPointer?) {` and other small stuff? – user924 Mar 27 '19 at 22:29
  • @user924 there is a sample project link at the bottom of the answer. You can putt it all in the same file Reachability.swift – Leo Dabus Mar 27 '19 at 23:15
  • @LeoDabus thanks. `.wwan` is Cellular network connection? – user924 Mar 28 '19 at 16:29
  • 1
    green means wifi connection, yellow means celular, red means no connection – Leo Dabus Mar 28 '19 at 16:31
  • @LeoDabus `if Network.reachability.status == .unreachable {` crashes with `Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value` – user924 Mar 28 '19 at 16:51
  • Have you ever initialised `Network.reachability`? You need to do first `do { try Network.reachability = Reachability(hostname: "www.yourApiDomain.com")` – Leo Dabus Mar 28 '19 at 16:52
  • @LeoDabus do I have to set some site address to check if I have wifi connection? – user924 Mar 28 '19 at 16:58
  • You don't. The kind of connection it is not related at all. But you can use `try Network.reachability = Reachability()` as well – Leo Dabus Mar 28 '19 at 16:59
  • @LeoDabus then I think it's better to remove this parameter from constructor. Some developers can still add it and there can be situations when specific host isn't accessible in some countries or by some internet providers – user924 Mar 28 '19 at 17:11
  • @user924 I don’t know what you mean by removing the parameter. There is two initializers. Use the one that fits your purpose. You can also delete the other one which you don’t need. – Leo Dabus Mar 28 '19 at 17:13
  • @LeoDabus I tried `try Network.reachability = Reachability()` and iPhone Simulator says that I don't have connection even though the connection is ok, condition `Network.reachability?.status == .unreachable` is true – user924 Mar 28 '19 at 17:15
  • You need to test it in a real device – Leo Dabus Mar 28 '19 at 17:16
  • 1
    @LeoDabus it is so difficult and weird after Android. In Android emulators standard tiny method to check internet connection works fine. If I don't pass any host for `Reachability()` will it work ok everywhere? I don't want to set `www.google.com` because it's blocked in China, for example... It would be really bad if user has connection but our app failed to detect because such host isn't accessible for that user – user924 Mar 28 '19 at 17:23
258

For Swift 3, Swift 4 (working with cellular and Wi-Fi):

import SystemConfiguration

public class Reachability {

    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(MemoryLayout.size(ofValue: zeroAddress))
        zeroAddress.sin_family = sa_family_t(AF_INET)

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

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

        /* Only Working for WIFI
        let isReachable = flags == .reachable
        let needsConnection = flags == .connectionRequired

        return isReachable && !needsConnection
        */

        // Working for Cellular and WIFI
        let isReachable = (flags.rawValue & UInt32(kSCNetworkFlagsReachable)) != 0
        let needsConnection = (flags.rawValue & UInt32(kSCNetworkFlagsConnectionRequired)) != 0
        let ret = (isReachable && !needsConnection)

        return ret

    }
}

Usage:

if Reachability.isConnectedToNetwork(){
    print("Internet Connection Available!")
}else{
    print("Internet Connection not Available!")
}
Tamás Sengel
  • 47,657
  • 24
  • 144
  • 178
Rajamohan S
  • 6,709
  • 5
  • 31
  • 51
  • 3
    This solution + CoreTelephony (CTTelephonyNetworkInfo().currentRadioAccessTechnology) are the best solution to get the current network type, and without needed to depend on third party libs. – Alejandro Jiménez Agudo Oct 23 '16 at 18:13
  • @AlejandroJiménezAgudo , Thank you for the info. i will try it :) – Rajamohan S Oct 24 '16 at 02:07
  • 3
    Works for Cellular and WiFi with swift 3.1! Works tested on iPhone 6 and 7. If Cellular Off I get "..Connection not Available". If I turn on airplane mode get the same "...not available". If I keep cellular off and turn on wifi I get ".. Connection Available" and finally if I turn wifi off and turn Cellular on I get ".. Connection Available". Note my Cellular connection is LTE – Brian Apr 10 '17 at 22:34
  • 2
    Hi, this doesnt work on iOS 11, airplane mode on a physical device. When set to airplane mode, isReachable is still true... any idea why? – Lawrence Tan Jan 16 '18 at 07:57
  • @LawrenceTan, make sure your device not using Internet via USB – Rajamohan S Jan 16 '18 at 08:05
  • @RAJAMOHAN-S yes its not, its still returning true without USB. – Lawrence Tan Jan 16 '18 at 08:20
  • @RAJAMOHAN-S, I forgot to mention, your VPN has to be on. then turn on airplane mode. – Lawrence Tan Jan 17 '18 at 05:56
  • 3
    This is working perfect for me in all scenarios. It's also tested with iOS 12 running on iPhone 6. – r3dm4n Jun 26 '18 at 16:47
  • 6
    This doesn't work if I'm connected to a wifi network without internet connection – Nathan Barreto Jul 10 '18 at 19:35
  • 1
    @NathanMartins, Unfortunately we can check flag this much only. You need to check URLConnection while connecting with Wi-Fi. I will update asap – Rajamohan S Jul 10 '18 at 19:40
  • I wonder if anyone has augmented this code to check if a site is reachable? For instance, users might have a network connection but have yet to agree to the terms and conditions popup that the network requires before accessing any websites off the lan. – Dave Levy Feb 28 '19 at 22:58
  • Dont forget import SystemConfiguration – RJB Nov 25 '19 at 19:17
  • @DharmendraKumarRajan did you found any other solution? – Pressing_Keys_24_7 Sep 30 '20 at 05:54
  • @NathanBarreto I'm also facing this same issue, is there any solution for this or not? – Ronak Patel Oct 12 '20 at 05:52
  • @RonakPatel at that time I was using Alamofire, and I think the NetworkReachabilityManager mentioned in below anwsers helped me. I honestly dont remember what was the solution – Nathan Barreto Oct 20 '20 at 19:29
  • @NathanBarreto I tried the below option still facing the issue. – Ronak Patel Oct 23 '20 at 03:57
  • @Flash Look at this link--> It works with current versions of Swift... https://www.hackingwithswift.com/example-code/networking/how-to-check-for-internet-connectivity-using-nwpathmonitor – Neloy Kundu Dec 05 '20 at 08:10
  • This is by far the best solution here and works perfectly in both cases for me, thank you very much! – VladTM23 Mar 12 '21 at 08:45
  • This is awesome – hbtpoprock Mar 30 '21 at 11:09
64

If someone is already using Alamofire then -

struct Connectivity {
  static let sharedInstance = NetworkReachabilityManager()!
  static var isConnectedToInternet:Bool {
      return self.sharedInstance.isReachable
    }
}

Usage:

if Connectivity.isConnectedToInternet {
     print("Connected")
 } else {
     print("No Internet")
}
Jack
  • 10,795
  • 4
  • 65
  • 90
  • 3
    from the Alamofire documentation: > The NetworkReachabilityManager class listens for reachability changes of hosts and addresses for both WWAN and WiFi network interfaces. > Reachability can be used to determine background information about why a network operation failed, or to retry network requests when a connection is established. **It should not be used to prevent a user from initiating a network request, as it’s possible that an initial request may be required to establish reachability.** – fespinozacast May 28 '18 at 12:46
  • 1
    @fespinozacast well you can check internet connection code in [NetworkReachabilityManager.swift](https://github.com/Alamofire/Alamofire/blob/master/Source/NetworkReachabilityManager.swift) , which is totally fine :) – Jack Jul 26 '18 at 11:10
  • 1
    Best solution! Thanks! – VyacheslavBakinkskiy Dec 04 '20 at 06:44
58

Create a new Swift file within your project, name it Reachability.swift. Cut & paste the following code into it to create your class.

import Foundation
import SystemConfiguration

public class Reachability {

    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(kCFAllocatorDefault, UnsafePointer($0))
        }

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

        let isReachable = flags == .Reachable
        let needsConnection = flags == .ConnectionRequired

        return isReachable && !needsConnection

    }
}

You can check internet connection anywhere in your project using this code:

if Reachability.isConnectedToNetwork() {
    println("Internet connection OK")
} else {
    println("Internet connection FAILED")
}

If the user is not connected to the internet, you may want to show them an alert dialog to notify them.

if Reachability.isConnectedToNetwork() {
    println("Internet connection OK")
} else {
    println("Internet connection FAILED")
    var alert = UIAlertView(title: "No Internet Connection", message: "Make sure your device is connected to the internet.", delegate: nil, cancelButtonTitle: "OK")
    alert.show()
}

Explanation:

We are making a reusable public class and a method which can be used anywhere in the project to check internet connectivity. We require adding Foundation and System Configuration frameworks.

In the public class Reachability, the method isConnectedToNetwork() -> Bool { } will return a bool value about internet connectivity. We use a if loop to perform required actions on case. I hope this is enough. Cheers!

Saranjith
  • 9,547
  • 2
  • 55
  • 102
A.G
  • 13,048
  • 84
  • 61
  • 4
    When you are giving a solution,please try to explain it a bit. Giving solution is not important , but making your readers understand the solution & its concept is more important. – G.Abhisek Dec 15 '15 at 05:44
  • 1
    I am new to iOS programming . Why do we need to initialise a C struct for the purpose. In which instances can we use C structures? – G.Abhisek Dec 15 '15 at 06:08
  • 1
    i put this on "viewdidload" only for test. I open app and OK, have internet, but when i shootdown my internet, it continues show me i have internet... this is persistent ? or i have to "call action" everytime ? – Daniel Arantes Loverde Feb 15 '16 at 18:19
  • 20
    @AlvinGeorge this does **NOT** work when connected to a cellular network. This only works when using wifi. To anyone else - you do not want to use this in your app! @ Alvin - can I suggest you update your answer, or remove it? With 19 up votes it is very misleading to other developers that could fall into the same trap. – Jordan Smith Mar 02 '16 at 20:16
  • @Jordan : This method is using default apple framework for network status. It may change with new iOS versions. So I recommend you to check and implement the Ashely Mill's Reachability class. I have done a dependency free implementation and shared the link above. Please have a look. – A.G Mar 03 '16 at 06:35
  • @Jordan did you find an approach that does work in both cases i.e. wifi and cellular? – user2363025 Apr 27 '16 at 14:12
  • this solution is not valid for cellular – Zafer Celaloglu Jun 03 '16 at 21:31
  • This does NOT detect when connected to cellular, only returns true if connected over Wifi – Steven B. Aug 03 '16 at 07:51
  • Do NOT use this as Apple will not approve your app as it is not IPv6 compliant like this. – checklist May 24 '17 at 13:35
  • You don't need to use the equality operator when a function returns a Bool. You don't need to say `Reachability.isConnectedToNetwork() == true` because the function itself returns a bool already. – meow2x Dec 13 '18 at 02:29
  • It's not working for mobile data in iOS 14 & above. – Dharmendra Kumar Rajan Sep 27 '20 at 13:08
18

Apple has introduced Network Framework in iOS12.

import Foundation
import Network

class NetworkReachability {

   var pathMonitor: NWPathMonitor!
   var path: NWPath?
   lazy var pathUpdateHandler: ((NWPath) -> Void) = { path in
    self.path = path
    if path.status == NWPath.Status.satisfied {
        print("Connected")
    } else if path.status == NWPath.Status.unsatisfied {
        print("unsatisfied")
    } else if path.status == NWPath.Status.requiresConnection {
        print("requiresConnection")
    } 
}

let backgroudQueue = DispatchQueue.global(qos: .background)

init() {
    pathMonitor = NWPathMonitor()
    pathMonitor.pathUpdateHandler = self.pathUpdateHandler
    pathMonitor.start(queue: backgroudQueue)
   } 

 func isNetworkAvailable() -> Bool {
        if let path = self.path {
           if path.status == NWPath.Status.satisfied {
            return true
          }
        }
       return false
   }
 }
Marcos Reboucas
  • 2,923
  • 24
  • 33
Yogendra Singh
  • 1,513
  • 20
  • 15
  • var path: NWPath? is nil. How do we initialize this property? – Vitya Shurapov May 06 '19 at 10:24
  • @VityaShurapov By assigning path you are getting in pathUpdateHandler block. – Yogendra Singh May 07 '19 at 10:00
  • 1
    This looks fancy but i found (at least on the simulator) that if i switch my laptop wifi off then on, i get a callback but it says 'unsatisfied', and after that the callback is never invoked. – zaitsman May 22 '19 at 07:27
  • When cellular cycles on/off, the handler only triggers once for on/off. When WiFi cycles on/off, I get 2 identical triggers for on/off (all properties are identical). Why is that? – geohei Jul 12 '19 at 14:09
  • `pathUpdateHandler` will introduce a strong reference with `self`, add `[weak self]` to solve it :) – Andrea de Marco Oct 11 '19 at 20:54
  • How can I define the hostname to use with NWPathMonitor? I want to do what `Reachability.reachabilityWithHostName` did. – agirault Jul 03 '20 at 17:30
16

I have checked out implementing Ashley Mill's Reachability class without Cocoa Pods/Dependancy Manager. The idea is to make the Reachability dependency free in the project.

Xcode 7.2 - Swift 2.1

1) https://github.com/ashleymills/Reachability.swift. Download add the Reachability class to the project .

Note: While adding, please make sure 'copy items if needed' is ticked.

2) Make an AppManager.swift class . This class will cater as Public Model class where public methods & data will be added and can be utilised in any VC.

//  AppManager.swift

import UIKit
import Foundation

class AppManager: NSObject{
    var delegate:AppManagerDelegate? = nil
    private var _useClosures:Bool = false
    private var reachability: Reachability?
    private var _isReachability:Bool = false
    private var _reachabiltyNetworkType :String?

    var isReachability:Bool {
        get {return _isReachability}
    }  
   var reachabiltyNetworkType:String {
    get {return _reachabiltyNetworkType! }
   }   




    // Create a shared instance of AppManager
    final  class var sharedInstance : AppManager {
        struct Static {
            static var instance : AppManager?
        }
        if !(Static.instance != nil) {
            Static.instance = AppManager()

        }
        return Static.instance!
    }

    // Reachability Methods
    func initRechabilityMonitor() {
        print("initialize rechability...")
        do {
            let reachability = try Reachability.reachabilityForInternetConnection()
            self.reachability = reachability
        } catch ReachabilityError.FailedToCreateWithAddress(let address) {
            print("Unable to create\nReachability with address:\n\(address)")
            return
        } catch {}
        if (_useClosures) {
            reachability?.whenReachable = { reachability in
                self.notifyReachability(reachability)
            }
            reachability?.whenUnreachable = { reachability in
                self.notifyReachability(reachability)
            }
        } else {
            self.notifyReachability(reachability!)
        }

        do {
            try reachability?.startNotifier()
        } catch {
            print("unable to start notifier")
            return
        }


    }        
    private func notifyReachability(reachability:Reachability) {
        if reachability.isReachable() {
            self._isReachability = true

//Determine Network Type 
      if reachability.isReachableViaWiFi() {   
        self._reachabiltyNetworkType = CONNECTION_NETWORK_TYPE.WIFI_NETWORK.rawValue
      } else {
        self._reachabiltyNetworkType = CONNECTION_NETWORK_TYPE.WWAN_NETWORK.rawValue
      }

        } else {
            self._isReachability = false
self._reachabiltyNetworkType = CONNECTION_NETWORK_TYPE.OTHER.rawValue

        }

        NSNotificationCenter.defaultCenter().addObserver(self, selector: "reachabilityChanged:", name: ReachabilityChangedNotification, object: reachability)
    }
    func reachabilityChanged(note: NSNotification) {
        let reachability = note.object as! Reachability
        dispatch_async(dispatch_get_main_queue()) {
            if (self._useClosures) {
                self.reachability?.whenReachable = { reachability in
                    self.notifyReachability(reachability)
                }
                self.reachability?.whenUnreachable = { reachability in
                    self.notifyReachability(reachability)
                }
            } else {
                self.notifyReachability(reachability)
            }
            self.delegate?.reachabilityStatusChangeHandler(reachability)
        }
    }
    deinit {
        reachability?.stopNotifier()
        if (!_useClosures) {
            NSNotificationCenter.defaultCenter().removeObserver(self, name: ReachabilityChangedNotification, object: nil)
        }
    }
}

3) Make a Delegate Class. I use delegate method to notify the connectivity status.

//  Protocols.swift

import Foundation
@objc protocol AppManagerDelegate:NSObjectProtocol {

    func reachabilityStatusChangeHandler(reachability:Reachability)
}

4) Make Parent class of UIViewController (Inheritance method). The parent class have methods which are accessible all child VCs.

//  UIappViewController.swift

    import UIKit

    class UIappViewController: UIViewController,AppManagerDelegate {
        var manager:AppManager = AppManager.sharedInstance

        override func viewDidLoad() {
            super.viewDidLoad()
            manager.delegate = self
        }
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
        }
        func reachabilityStatusChangeHandler(reachability: Reachability) {
            if reachability.isReachable() {
                print("isReachable")
            } else {
                print("notReachable")
            }
        }
    }

5) Start Real time Internet Connectivity Monitoring in AppDelegate.

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    AppManager.sharedInstance.initRechabilityMonitor()
return true
}

6) I have added a Swift File Name AppReference to store constant enum values.

//  AppReference.swift

import Foundation

enum CONNECTION_NETWORK_TYPE : String {

  case WIFI_NETWORK = "Wifi"
  case WWAN_NETWORK = "Cellular"
  case OTHER = "Other"

}

7) On ViewController (ex. You want to call an API only if network is available)

//  ViewController.swift

        import UIKit

class ViewController: UIappViewController {
  var reachability:Reachability?

  override func viewDidLoad() {
    super.viewDidLoad()
    manager.delegate = self

    if(AppManager.sharedInstance.isReachability)
    {
      print("net available")
      //call API from here.

    } else {
      dispatch_async(dispatch_get_main_queue()) {
        print("net not available")
        //Show Alert
      }
    }


    //Determine Network Type
    if(AppManager.sharedInstance.reachabiltyNetworkType == "Wifi")
    {
      print(".Wifi")
    }
    else if (AppManager.sharedInstance.reachabiltyNetworkType == "Cellular")
    {
      print(".Cellular")
    }
    else {
      dispatch_async(dispatch_get_main_queue()) {
        print("Network not reachable")
      }
    }

  }
  override func viewWillAppear(animated: Bool) {
  }
  override func didReceiveMemoryWarning() {
  }
}

The sample can be downloaded @ https://github.com/alvinreuben/Reachability-Sample

Upgraded to Swift 3.1- https://github.com/alvinvgeorge/Reachability-UpgradedToSwift3

A.G
  • 13,048
  • 84
  • 61
  • In Step 4... the UIappViewController.swift, why do we need this class!? – Learn2Code Apr 01 '17 at 23:52
  • UIappViewController.swift does not work with UICollectionViewController, so if you have a View that is of UICollectionViewController , you cant inherit UIappViewController!? – Learn2Code Apr 01 '17 at 23:54
14

Although it does not directly answers your question, I would like to mention Apple recentely had this talk:

https://developer.apple.com/videos/play/wwdc2018/714/

At around 09:55 he talks about doing this stuff you are asking about:

  1. Check connection
  2. If connection -> Do something
  3. If no connection -> Do something else (wait? retry?)

However, this has a few pitfalls:

  • What if in step 2 it says it has a connection, but 0.5 seconds later he hasn't?
  • What if the user is behind a proxy
  • Last but not least, what if some answers here can not determine the connectivity right? (I am sure that if you rapidly switch your connection, go to wi-fi and turn it off (just make it complicated), it almost never can correctly determine whetever I got a connection or not).
  • Quote from the video: "There is no way to guarentee whether a future operation will succeed or not"

The following points are some best practices according to Apple:

According to the talk, there shouldn't be any reason to pre-check whetever you got internet connection or not, since it may not be accurate at the time you send your request to the server.

J. Doe
  • 9,757
  • 4
  • 42
  • 83
13

Use this for Swift-5+

import Foundation
import UIKit
import SystemConfiguration

public class InternetConnectionManager {
    
    
    private init() {
        
    }
    
    public static 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) {
                
                SCNetworkReachabilityCreateWithAddress(nil, $0)
                
            }
            
        }) else {
            
            return false
        }
        var flags = SCNetworkReachabilityFlags()
        if !SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) {
            return false
        }
        let isReachable = (flags.rawValue & UInt32(kSCNetworkFlagsReachable)) != 0
        let needsConnection = (flags.rawValue & UInt32(kSCNetworkFlagsConnectionRequired)) != 0
        return (isReachable && !needsConnection)
    }
    
}

Usage:

if InternetConnectionManager.isConnectedToNetwork(){
    print("Connected")
}else{
    print("Not Connected")
}

Or Just use this framework for more Utilities: Link

Pradeep Reddy Kypa
  • 3,736
  • 7
  • 49
  • 73
Jamil Hasnine Tamim
  • 3,459
  • 20
  • 39
7

Just figured this out for myself.

Xcode: 7.3.1, iOS 9.3.3

Using ashleymills/Reachability.swift as Reachability.swift in my project, I created the following function:

func hasConnectivity() -> Bool {
    do {
        let reachability: Reachability = try Reachability.reachabilityForInternetConnection()
        let networkStatus: Int = reachability.currentReachabilityStatus.hashValue

        return (networkStatus != 0)
    }
    catch {
        // Handle error however you please
        return false
    }
}

Simply call hasConnectivity() where ever you need to check for a connection. This works for Wifi as well as Cellular.


Adding ashleymills's Reachability.swift so people dont have to move between sites:

Copyright (c) 2014, Ashley Mills
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/

// Reachability.swift version 2.2beta2

import SystemConfiguration
import Foundation

public enum ReachabilityError: ErrorType {
    case FailedToCreateWithAddress(sockaddr_in)
    case FailedToCreateWithHostname(String)
    case UnableToSetCallback
    case UnableToSetDispatchQueue
}

public let ReachabilityChangedNotification = "ReachabilityChangedNotification"

func callback(reachability:SCNetworkReachability, flags: SCNetworkReachabilityFlags, info: UnsafeMutablePointer<Void>) {
    let reachability = Unmanaged<Reachability>.fromOpaque(COpaquePointer(info)).takeUnretainedValue()

    dispatch_async(dispatch_get_main_queue()) {
        reachability.reachabilityChanged(flags)
    }
}


public class Reachability: NSObject {

    public typealias NetworkReachable = (Reachability) -> ()
    public typealias NetworkUnreachable = (Reachability) -> ()

    public enum NetworkStatus: CustomStringConvertible {

        case NotReachable, ReachableViaWiFi, ReachableViaWWAN

        public var description: String {
            switch self {
            case .ReachableViaWWAN:
                return "Cellular"
            case .ReachableViaWiFi:
                return "WiFi"
            case .NotReachable:
                return "No Connection"
            }
        }
    }

    // MARK: - *** Public properties ***
    public var whenReachable: NetworkReachable?
    public var whenUnreachable: NetworkUnreachable?
    public var reachableOnWWAN: Bool
    public var notificationCenter = NSNotificationCenter.defaultCenter()

    public var currentReachabilityStatus: NetworkStatus {
        if isReachable() {
            if isReachableViaWiFi() {
                return .ReachableViaWiFi
            }
            if isRunningOnDevice {
                return .ReachableViaWWAN
            }
        }
        return .NotReachable
    }

    public var currentReachabilityString: String {
        return "\(currentReachabilityStatus)"
    }

    private var previousFlags: SCNetworkReachabilityFlags?

    // MARK: - *** Initialisation methods ***

    required public init(reachabilityRef: SCNetworkReachability) {
        reachableOnWWAN = true
        self.reachabilityRef = reachabilityRef
    }

    public convenience init(hostname: String) throws {

        let nodename = (hostname as NSString).UTF8String
        guard let ref = SCNetworkReachabilityCreateWithName(nil, nodename) else { throw ReachabilityError.FailedToCreateWithHostname(hostname) }

        self.init(reachabilityRef: ref)
    }

    public class func reachabilityForInternetConnection() throws -> Reachability {

        var zeroAddress = sockaddr_in()
        zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress))
        zeroAddress.sin_family = sa_family_t(AF_INET)

        guard let ref = withUnsafePointer(&zeroAddress, {
            SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0))
        }) else { throw ReachabilityError.FailedToCreateWithAddress(zeroAddress) }

        return Reachability(reachabilityRef: ref)
    }

    public class func reachabilityForLocalWiFi() throws -> Reachability {

        var localWifiAddress: sockaddr_in = sockaddr_in(sin_len: __uint8_t(0), sin_family: sa_family_t(0), sin_port: in_port_t(0), sin_addr: in_addr(s_addr: 0), sin_zero: (0, 0, 0, 0, 0, 0, 0, 0))
        localWifiAddress.sin_len = UInt8(sizeofValue(localWifiAddress))
        localWifiAddress.sin_family = sa_family_t(AF_INET)

        // IN_LINKLOCALNETNUM is defined in <netinet/in.h> as 169.254.0.0
        let address: UInt32 = 0xA9FE0000
        localWifiAddress.sin_addr.s_addr = in_addr_t(address.bigEndian)

        guard let ref = withUnsafePointer(&localWifiAddress, {
            SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0))
        }) else { throw ReachabilityError.FailedToCreateWithAddress(localWifiAddress) }

        return Reachability(reachabilityRef: ref)
    }

    // MARK: - *** Notifier methods ***
    public func startNotifier() throws {

        guard !notifierRunning else { return }

        var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
        context.info = UnsafeMutablePointer(Unmanaged.passUnretained(self).toOpaque())

        if !SCNetworkReachabilitySetCallback(reachabilityRef!, callback, &context) {
            stopNotifier()
            throw ReachabilityError.UnableToSetCallback
        }

        if !SCNetworkReachabilitySetDispatchQueue(reachabilityRef!, reachabilitySerialQueue) {
            stopNotifier()
            throw ReachabilityError.UnableToSetDispatchQueue
        }

        // Perform an intial check
        dispatch_async(reachabilitySerialQueue) { () -> Void in
            let flags = self.reachabilityFlags
            self.reachabilityChanged(flags)
        }

        notifierRunning = true
    }

    public func stopNotifier() {
        defer { notifierRunning = false }
        guard let reachabilityRef = reachabilityRef else { return }

        SCNetworkReachabilitySetCallback(reachabilityRef, nil, nil)
        SCNetworkReachabilitySetDispatchQueue(reachabilityRef, nil)
    }

    // MARK: - *** Connection test methods ***
    public func isReachable() -> Bool {
        let flags = reachabilityFlags
        return isReachableWithFlags(flags)
    }

    public func isReachableViaWWAN() -> Bool {

        let flags = reachabilityFlags

        // Check we're not on the simulator, we're REACHABLE and check we're on WWAN
        return isRunningOnDevice && isReachable(flags) && isOnWWAN(flags)
    }

    public func isReachableViaWiFi() -> Bool {

        let flags = reachabilityFlags

        // Check we're reachable
        if !isReachable(flags) {
            return false
        }

        // Must be on WiFi if reachable but not on an iOS device (i.e. simulator)
        if !isRunningOnDevice {
            return true
        }

        // Check we're NOT on WWAN
        return !isOnWWAN(flags)
    }

    // MARK: - *** Private methods ***
    private var isRunningOnDevice: Bool = {
        #if (arch(i386) || arch(x86_64)) && os(iOS)
            return false
        #else
            return true
        #endif
    }()

    private var notifierRunning = false
    private var reachabilityRef: SCNetworkReachability?
    private let reachabilitySerialQueue = dispatch_queue_create("uk.co.ashleymills.reachability", DISPATCH_QUEUE_SERIAL)

    private func reachabilityChanged(flags: SCNetworkReachabilityFlags) {

        guard previousFlags != flags else { return }

        if isReachableWithFlags(flags) {
            if let block = whenReachable {
                block(self)
            }
        } else {
            if let block = whenUnreachable {
                block(self)
            }
        }

        notificationCenter.postNotificationName(ReachabilityChangedNotification, object:self)

        previousFlags = flags
    }

    private func isReachableWithFlags(flags: SCNetworkReachabilityFlags) -> Bool {

        if !isReachable(flags) {
            return false
        }

        if isConnectionRequiredOrTransient(flags) {
            return false
        }

        if isRunningOnDevice {
            if isOnWWAN(flags) && !reachableOnWWAN {
                // We don't want to connect when on 3G.
                return false
            }
        }

        return true
    }

    // WWAN may be available, but not active until a connection has been established.
    // WiFi may require a connection for VPN on Demand.
    private func isConnectionRequired() -> Bool {
        return connectionRequired()
    }

    private func connectionRequired() -> Bool {
        let flags = reachabilityFlags
        return isConnectionRequired(flags)
    }

    // Dynamic, on demand connection?
    private func isConnectionOnDemand() -> Bool {
        let flags = reachabilityFlags
        return isConnectionRequired(flags) && isConnectionOnTrafficOrDemand(flags)
    }

    // Is user intervention required?
    private func isInterventionRequired() -> Bool {
        let flags = reachabilityFlags
        return isConnectionRequired(flags) && isInterventionRequired(flags)
    }

    private func isOnWWAN(flags: SCNetworkReachabilityFlags) -> Bool {
        #if os(iOS)
            return flags.contains(.IsWWAN)
        #else
            return false
        #endif
    }
    private func isReachable(flags: SCNetworkReachabilityFlags) -> Bool {
        return flags.contains(.Reachable)
    }
    private func isConnectionRequired(flags: SCNetworkReachabilityFlags) -> Bool {
        return flags.contains(.ConnectionRequired)
    }
    private func isInterventionRequired(flags: SCNetworkReachabilityFlags) -> Bool {
        return flags.contains(.InterventionRequired)
    }
    private func isConnectionOnTraffic(flags: SCNetworkReachabilityFlags) -> Bool {
        return flags.contains(.ConnectionOnTraffic)
    }
    private func isConnectionOnDemand(flags: SCNetworkReachabilityFlags) -> Bool {
        return flags.contains(.ConnectionOnDemand)
    }
    func isConnectionOnTrafficOrDemand(flags: SCNetworkReachabilityFlags) -> Bool {
        return !flags.intersect([.ConnectionOnTraffic, .ConnectionOnDemand]).isEmpty
    }
    private func isTransientConnection(flags: SCNetworkReachabilityFlags) -> Bool {
        return flags.contains(.TransientConnection)
    }
    private func isLocalAddress(flags: SCNetworkReachabilityFlags) -> Bool {
        return flags.contains(.IsLocalAddress)
    }
    private func isDirect(flags: SCNetworkReachabilityFlags) -> Bool {
        return flags.contains(.IsDirect)
    }
    private func isConnectionRequiredOrTransient(flags: SCNetworkReachabilityFlags) -> Bool {
        let testcase:SCNetworkReachabilityFlags = [.ConnectionRequired, .TransientConnection]
        return flags.intersect(testcase) == testcase
    }

    private var reachabilityFlags: SCNetworkReachabilityFlags {

        guard let reachabilityRef = reachabilityRef else { return SCNetworkReachabilityFlags() }

        var flags = SCNetworkReachabilityFlags()
        let gotFlags = withUnsafeMutablePointer(&flags) {
            SCNetworkReachabilityGetFlags(reachabilityRef, UnsafeMutablePointer($0))
        }

        if gotFlags {
            return flags
        } else {
            return SCNetworkReachabilityFlags()
        }
    }

    override public var description: String {

        var W: String
        if isRunningOnDevice {
            W = isOnWWAN(reachabilityFlags) ? "W" : "-"
        } else {
            W = "X"
        }
        let R = isReachable(reachabilityFlags) ? "R" : "-"
        let c = isConnectionRequired(reachabilityFlags) ? "c" : "-"
        let t = isTransientConnection(reachabilityFlags) ? "t" : "-"
        let i = isInterventionRequired(reachabilityFlags) ? "i" : "-"
        let C = isConnectionOnTraffic(reachabilityFlags) ? "C" : "-"
        let D = isConnectionOnDemand(reachabilityFlags) ? "D" : "-"
        let l = isLocalAddress(reachabilityFlags) ? "l" : "-"
        let d = isDirect(reachabilityFlags) ? "d" : "-"

        return "\(W)\(R) \(c)\(t)\(i)\(C)\(D)\(l)\(d)"
    }

    deinit {
        stopNotifier()

        reachabilityRef = nil
        whenReachable = nil
        whenUnreachable = nil
    }
}
cmeadows
  • 170
  • 2
  • 12
  • @Xitcod13 This would work with a 4G Network. That being said it is recommended NOT to test preflight. Handle the exception thrown in your attempt to connect instead of using a Reachability.swift test case. – cmeadows Aug 23 '16 at 15:51
  • https://github.com/justin/Reachability.swift/tree/feature/swift3.0 for the swift3 – AMAN77 Oct 05 '16 at 09:35
  • Reachabilty.swift has issues with Swift 3 – Famic Tech Dec 24 '16 at 05:27
  • Works well with the Swift 3, just change `let reachability: Reachability = try Reachability.reachabilityForInternetConnection()` to `let reachability: Reachability = try Reachability()!` – davidethell Mar 14 '17 at 11:34
4

Swift 5

import SystemConfiguration    

protocol Utilities {}
extension NSObject: Utilities {
    enum ReachabilityStatus {
        case notReachable
        case reachableViaWWAN
        case reachableViaWiFi
    }
    
    var currentReachabilityStatus: ReachabilityStatus {
        
        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 .notReachable
        }
        
        var flags: SCNetworkReachabilityFlags = []
        if !SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) {
            return .notReachable
        }
        
        if flags.contains(.reachable) == false {
            // The target host is not reachable.
            return .notReachable
        }
        else if flags.contains(.isWWAN) == true {
            // WWAN connections are OK if the calling application is using the CFNetwork APIs.
            return .reachableViaWWAN
        }
        else if flags.contains(.connectionRequired) == false {
            // If the target host is reachable and no connection is required then we'll assume that you're on Wi-Fi...
            return .reachableViaWiFi
        }
        else if (flags.contains(.connectionOnDemand) == true || flags.contains(.connectionOnTraffic) == true) && flags.contains(.interventionRequired) == false {
            // The connection is on-demand (or on-traffic) if the calling application is using the CFSocketStream or higher APIs and no [user] intervention is needed
            return .reachableViaWiFi
        }
        else {
            return .notReachable
        }
    }
}

In any method use the below condition

if currentReachabilityStatus == .notReachable {
    // Network Unavailable
} else {
    // Network Available
}
Olcay Ertaş
  • 5,374
  • 8
  • 71
  • 98
Gurjinder Singh
  • 5,576
  • 44
  • 41
4

iOS12 Swift 4 and Swift 5

If you just want to check the connection, and your lowest target is iOS12, then you can use NWPathMonitor

import Network

It needs a little setup with some properties.

let internetMonitor = NWPathMonitor()
let internetQueue = DispatchQueue(label: "InternetMonitor")
private var hasConnectionPath = false

I created a function to get it going. You can do this on view did load or anywhere else. I put a guard in so you can call it all you want to get it going.

func startInternetTracking() {
    // only fires once
    guard internetMonitor.pathUpdateHandler == nil else {
        return
    }
    internetMonitor.pathUpdateHandler = { update in
        if update.status == .satisfied {
            print("Internet connection on.")
            self.hasConnectionPath = true
        } else {
            print("no internet connection.")
            self.hasConnectionPath = false
        }
    }
    internetMonitor.start(queue: internetQueue)
}

/// will tell you if the device has an Internet connection
/// - Returns: true if there is some kind of connection
func hasInternet() -> Bool {
    return hasConnectionPath
}

Now you can just call the helper function hasInternet() to see if you have one. It updates in real time. See Apple documentation for NWPathMonitor. It has lots more functionality like cancel() if you need to stop tracking the connection, type of internet you are looking for, etc. https://developer.apple.com/documentation/network/nwpathmonitor

skymook
  • 2,777
  • 27
  • 34
  • As others have indicated above, this method does not update constantly so if the connection is restored, the check will contine to be false for a while. To get around this, the .cancel() may be used when dismissing or navigating off the view, then it will be reinitiated when returning(if used in viewDidLoad). The good thing is that if the connection is lost after initiating, the hasConnectionPath appears to change immediately. So if you are protecting from a crash, this does the job. – Markv07 Jan 18 '21 at 13:07
  • I have not experienced the delay myself. I keep alive my implementation of `pathUpdateHandler`, and other variables above, in a singleton that is always available. I wonder, @Markv07 if your `pathUpdateHandler` closure is being destroyed and recreated? The first time you create the `pathUpdateHandler` takes a little while. – skymook Jan 19 '21 at 10:31
3

As of iOS 12, NWPathMonitor replaced Reachability. Use this:

import Network


struct Internet {
 
 private static let monitor = NWPathMonitor()
 
 static var active = false
 static var expensive = false
 
 /// Monitors internet connectivity changes. Updates with every change in connectivity.
 /// Updates variables for availability and if it's expensive (cellular).
 static func start() {
  guard monitor.pathUpdateHandler == nil else { return }
  
  monitor.pathUpdateHandler = { update in
   Internet.active = update.status == .satisfied ? true : false
   Internet.expensive = update.isExpensive ? true : false
  }
  
  monitor.start(queue: DispatchQueue(label: "InternetMonitor"))
 }
 
}

In use:

Internet.start()

if Internet.active {
 // do something
}
  
if Internet.expensive {
 // device is using Cellular data or WiFi hotspot
}
Bobby
  • 5,406
  • 3
  • 30
  • 35
2

I have made my own solution using NSTimer and Alamofire:

import Alamofire

public class ConnectionHelper: NSObject {
    var request: Alamofire.Request?

    func isInternetConnected(completionHandler: Bool -> Void) {
        NSTimer.scheduledTimerWithTimeInterval(5.0, target: self, selector: "requestTimeout", userInfo: nil, repeats: false)

        request = Alamofire
            .request(
                Method.HEAD,
                "http://www.testurl.com"
            )
            .response { response in
                if response.3?.code == -999 {
                    completionHandler(
                        false
                    )
                } else {
                    completionHandler(
                        true
                    )
                }
        }
    }

    func requestTimeout() {
        request!.cancel()
    }
}

The NSTimer is used as a timeout, and was used due to unreliable results using the Alamofire timeout. The request should be made to a URL you trust to be reliable, such as your own server or the server that is hosting the services you depend on.

When the timer expires, the request is cancelled and the results are returned using a completion handler.

Usage:

ConnectionHelper().isInternetConnected() { internetConnected in
    if internetConnected {
        // Connected
    } else {
        // Not connected
    }
}
Mark Tickner
  • 925
  • 2
  • 14
  • 26
  • 1
    What if the website server is down? – TechBee Feb 02 '16 at 12:17
  • Then ```requestTimeout()``` would be called after the specified timeout was reached – Mark Tickner Feb 02 '16 at 12:21
  • Thanks Mark! Your approach should be generic!! – TechBee Feb 02 '16 at 12:27
  • 1
    In what way? The site that is used? The idea would be that the site used is one that your app is dependent on – Mark Tickner Feb 02 '16 at 14:21
  • Probably not a good idea to make a network connection and see if it comes back with a response or not. There are simply too many _what if questions_ – GoodSp33d Apr 10 '16 at 12:25
  • 1
    @GoodSp33d This method is good for checking if a web service is running or not. You may have an internet connection, but if a web service is down, you may want to allow the app to behave as if they have no network connection (eg. working offline). – Colin Basnett Jul 08 '16 at 19:48
2

If you are using Alamofire, you can do something like this:

let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
configuration.timeoutIntervalForRequest = 15 //Set timeouts in sec
configuration.timeoutIntervalForResource = 15

let alamoFireManager = Alamofire.Manager(configuration:configuration)
alamoFireManager?.request(.GET, "https://yourURL.com", parameters: headers, encoding: .URL)
                     .validate()
                              .responseJSON { response in

                                if let error = response.result.error {
                                   switch error.code{
                                    case -1001:
                                        print("Slow connection")
                                        return
                                    case -1009:
                                        print("No Connection!")
                                        return
                                    default: break
                                    }
                                }
Mo Abdul-Hameed
  • 5,516
  • 2
  • 21
  • 32
Illya Krit
  • 725
  • 1
  • 6
  • 8
2

While it may not directly determine whether the phone is connected to a network, the simplest(, cleanest?) solution would be to 'ping' Google, or some other server, (which isn't possible unless the phone is connected to a network):

private var urlSession:URLSession = {
    var newConfiguration:URLSessionConfiguration = .default
    newConfiguration.waitsForConnectivity = false
    newConfiguration.allowsCellularAccess = true
    return URLSession(configuration: newConfiguration)
}()

public func canReachGoogle() -> Bool
{
    let url = URL(string: "https://8.8.8.8")
    let semaphore = DispatchSemaphore(value: 0)
    var success = false
    let task = urlSession.dataTask(with: url!)
    { data, response, error in
        if error != nil
        {
            success = false
        }
        else
        {
            success = true
        }
        semaphore.signal()
    }

    task.resume()
    semaphore.wait()

    return success
}

If you're concerned that the server may be down or may block your IP, you can always ping multiple servers in a similar fashion and return whether any of them are reachable. Or have someone set up a dedicated server just for this purpose.

A. L. Strine
  • 347
  • 3
  • 17
2

if your project has a target above or equal iOS 12 and uses combine you could use this little piece of code.

import Combine
import Network

enum NerworkType {
    case wifi
    case cellular
    case loopBack
    case wired
    case other
}

protocol ReachabilityServiceContract {
    var reachabilityInfos: PassthroughSubject<NWPath, Never> { get set }
    var isNetworkAvailable: CurrentValueSubject<Bool, Never> { get set }
    var typeOfCurrentConnection: PassthroughSubject<NerworkType, Never> { get set }
}

final class ReachabilityService: ReachabilityServiceContract {
    var reachabilityInfos: PassthroughSubject<NWPath, Never> = .init()
    var isNetworkAvailable: CurrentValueSubject<Bool, Never> = .init(false)
    var typeOfCurrentConnection: PassthroughSubject<NerworkType, Never> = .init()

    private let monitor: NWPathMonitor
    private let backgroudQueue = DispatchQueue.global(qos: .background)

    init() {
        monitor = NWPathMonitor()
        setUp()
    }

    init(with interFaceType: NWInterface.InterfaceType) {
        monitor = NWPathMonitor(requiredInterfaceType: interFaceType)
        setUp()
    }

    deinit {
        monitor.cancel()
    }
}

private extension ReachabilityService {
    func setUp() {
    
        monitor.pathUpdateHandler = { [weak self] path in
            self?.reachabilityInfos.send(path)
            switch path.status {
            case .satisfied:
                self?.isNetworkAvailable.send(true)
            case .unsatisfied, .requiresConnection:
                self?.isNetworkAvailable.send(false)
            @unknown default:
                self?.isNetworkAvailable.send(false)
            }
            if path.usesInterfaceType(.wifi) {
                self?.typeOfCurrentConnection.send(.wifi)
            } else if path.usesInterfaceType(.cellular) {
                self?.typeOfCurrentConnection.send(.cellular)
            } else if path.usesInterfaceType(.loopback) {
                self?.typeOfCurrentConnection.send(.loopBack)
            } else if path.usesInterfaceType(.wiredEthernet) {
                self?.typeOfCurrentConnection.send(.wired)
            } else if path.usesInterfaceType(.other) {
                self?.typeOfCurrentConnection.send(.other)
            }
        }
    
        monitor.start(queue: backgroudQueue)
    }
}

Just subscribe to the variable you want to follow and you should be updated of any changes.

Dharman
  • 21,838
  • 18
  • 57
  • 107
Martin
  • 591
  • 4
  • 15
2

Swift 5 solution:

  1. Download ashleymills' Reachability class from https://github.com/ashleymills/Reachability.swift.
  2. Add the Reachability class to the project.
  3. Put following code in the your class maintaining the connection status
class ConnectionManager {

    static let shared = ConnectionManager()
    private init () {}

    func hasConnectivity() -> Bool {
        do {
            let reachability: Reachability = try Reachability()
            let networkStatus = reachability.connection
            
            switch networkStatus {
            case .unavailable:
                return false
            case .wifi, .cellular:
                return true
            }
        }
        catch {
            return false
        }
    }
}
  1. Use it like below:
ConnectionManager.shared.hasConnectivity()
Marybnq
  • 768
  • 8
  • 22
grow4gaurav
  • 1,869
  • 1
  • 5
  • 8
1

here my solution for swift 2.3 with the lib (Reachability.swift)

Go into your Podfile and add :

pod 'ReachabilitySwift', '~> 2.4' // swift 2.3

Then into your terminal :

pod install

Then create a new file ReachabilityManager and add code below :

import Foundation
import ReachabilitySwift

enum ReachabilityManagerType {
    case Wifi
    case Cellular
    case None
}

class ReachabilityManager {
    static let sharedInstance = ReachabilityManager()

    private var reachability: Reachability!
    private var reachabilityManagerType: ReachabilityManagerType = .None


    private init() {
        do {
            self.reachability = try Reachability.reachabilityForInternetConnection()
        } catch {
            print("Unable to create Reachability")
            return
        }

        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ReachabilityManager.reachabilityChanged(_:)),name: ReachabilityChangedNotification,object: self.reachability)
        do{
            try self.reachability.startNotifier()
        }catch{
            print("could not start reachability notifier")
        }
    }

    @objc private func reachabilityChanged(note: NSNotification) {

        let reachability = note.object as! Reachability

        if reachability.isReachable() {
            if reachability.isReachableViaWiFi() {
                self.reachabilityManagerType = .Wifi
            } else {
                self.reachabilityManagerType = .Cellular
            }
        } else {
            self.reachabilityManagerType = .None
        }
    }
}

extension ReachabilityManager {

    func isConnectedToNetwork() -> Bool {
        return reachabilityManagerType != .None
    }

}

How use it:

go into your AppDelegate.swift and add the code below :

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
     ReachabilityManager.sharedInstance
}

Then when you want to check if the device is connected to internet do :

if ReachabilityManager.sharedInstance.isConnectedToNetwork() {
   // Connected
} else {
  // Not connected
}
douarbou
  • 2,203
  • 1
  • 20
  • 25
1

For swift 3, I couldn't use just reachability from RAJAMOHAN-S solutions since it returns "true" if there is WiFi but no Internet. Thus, I implemented second validation via URLSession class and completion handler.

Here is the whole class.

import Foundation
import SystemConfiguration

public class Reachability {

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(MemoryLayout.size(ofValue: zeroAddress))
zeroAddress.sin_family = sa_family_t(AF_INET)

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

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

// Working for Cellular and WIFI
let isReachable = (flags.rawValue & UInt32(kSCNetworkFlagsReachable)) != 0
let needsConnection = (flags.rawValue & UInt32(kSCNetworkFlagsConnectionRequired)) != 0
let ret = (isReachable && !needsConnection)

return ret
}



class func isInternetAvailable(webSiteToPing: String?, completionHandler: @escaping (Bool) -> Void) {

// 1. Check the WiFi Connection
guard isConnectedToNetwork() else {
  completionHandler(false)
  return
}

// 2. Check the Internet Connection
var webAddress = "https://www.google.com" // Default Web Site
if let _ = webSiteToPing {
  webAddress = webSiteToPing!
}

guard let url = URL(string: webAddress) else {
  completionHandler(false)
  print("could not create url from: \(webAddress)")
  return
}

let urlRequest = URLRequest(url: url)
let session = URLSession.shared
let task = session.dataTask(with: urlRequest, completionHandler: { (data, response, error) in
  if error != nil || response == nil {
    completionHandler(false)
  } else {
    completionHandler(true)
  }
})

  task.resume()
}
}

And you call this like this, for example:

Reachability.isInternetAvailable(webSiteToPing: nil) { (isInternetAvailable) in
  guard isInternetAvailable else {
    // Inform user for example
    return
  }

  // Do some action if there is Internet
}
Pavle Mijatovic
  • 733
  • 8
  • 6
1

Create a new Swift file within your project, name it Reachability.swift. Cut & paste the following code into it to create your class.

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)
        
        let defaultRouteReachability = withUnsafePointer(to: &zeroAddress) {
            $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {zeroSockAddress in
                SCNetworkReachabilityCreateWithAddress(nil, zeroSockAddress)
            }
        }
        
        var flags = SCNetworkReachabilityFlags()
        if !SCNetworkReachabilityGetFlags(defaultRouteReachability!, &flags) {
            return false
        }
        let isReachable = (flags.rawValue & UInt32(kSCNetworkFlagsReachable)) != 0
        let needsConnection = (flags.rawValue & UInt32(kSCNetworkFlagsConnectionRequired)) != 0
        return (isReachable && !needsConnection)
    }
}

you can call Reachability from anywhere of your code, just like

if Reachability.isConnectedToNetwork() {
    print("Network is connected")
} else {
    print("Network is not connected")
}
Olcay Ertaş
  • 5,374
  • 8
  • 71
  • 98
Menon Hasan
  • 293
  • 1
  • 12
0

With the help of below code you can check for internet Connection for both cellular network as well as for wifi. language - Swift 3.0

import UIKit
import Foundation
import SystemConfiguration

class NetworkConnection: UIViewController {

  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(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) {
        SCNetworkReachabilityCreateWithAddress(nil, $0)
      }
    }) else {
      return false
    }

    var flags: SCNetworkReachabilityFlags = SCNetworkReachabilityFlags(rawValue: 0)
    if SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) == false {
      return false
    }
    let isReachable = (flags.rawValue & UInt32(kSCNetworkFlagsReachable)) != 0
    let needsConnection = (flags.rawValue & UInt32(kSCNetworkFlagsConnectionRequired)) != 0
    return (isReachable && !needsConnection)
  }

  class func checkConnection(sender:UIViewController){
    if NetworkConnection.isConnectedToNetwork() == true {
      print("Connected to the internet")
      //  Do something
    } else {
      print("No internet connection")
      let alertController = UIAlertController(title: "No Internet Available", message: "", preferredStyle: UIAlertControllerStyle.alert)
      let okAction = UIAlertAction(title: "Ok", style: UIAlertActionStyle.default){(result:UIAlertAction) -> Void in
        return
      }
      alertController.addAction(okAction)
      sender.present(alertController, animated: true, completion: nil)
      //  Do something
    }
  }

}
Ronit
  • 184
  • 1
  • 14
0
struct Connectivity {
    static let sharedInstance = NetworkReachabilityManager()!
    static var isConnectedToInternet:Bool {
        return self.sharedInstance.isReachable
    }
}

Now call it

if Connectivity.isConnectedToInternet{
    call_your_methods_here()
} else{
    show_alert_for_noInternet()
}
Olcay Ertaş
  • 5,374
  • 8
  • 71
  • 98
Raghib Arshi
  • 503
  • 4
  • 10
0

Updated version of @martin's answer for Swift 5+ using Combine. This one also includes unavailibity reason check for iOS 14.

import Combine
import Network

enum NetworkType {
    case wifi
    case cellular
    case loopBack
    case wired
    case other
}

final class ReachabilityService: ObservableObject {

    @Published var reachabilityInfos: NWPath?
    @Published var isNetworkAvailable: Bool?
    @Published var typeOfCurrentConnection: NetworkType?

    private let monitor = NWPathMonitor()
    private let backgroundQueue = DispatchQueue.global(qos: .background)

    init() {
        setUp()
    }

    init(with interFaceType: NWInterface.InterfaceType) {
        setUp()
    }

    deinit {
        monitor.cancel()
    }
}

private extension ReachabilityService {

    func setUp() {

        monitor.pathUpdateHandler = { [weak self] path in
            self?.reachabilityInfos = path
            switch path.status {
            case .satisfied:
                print("ReachabilityService: satisfied")
                self?.isNetworkAvailable = true
                break
            case .unsatisfied:
                print("ReachabilityService: unsatisfied")

                if #available(iOS 14.2, *) {
                    switch path.unsatisfiedReason {

                    case .notAvailable:
                        print("ReachabilityService: unsatisfiedReason: notAvailable")
                        break
                    case .cellularDenied:
                        print("ReachabilityService: unsatisfiedReason: cellularDenied")
                        break
                    case .wifiDenied:
                        print("ReachabilityService: unsatisfiedReason: wifiDenied")
                        break
                    case .localNetworkDenied:
                        print("ReachabilityService: unsatisfiedReason: localNetworkDenied")
                        break
                    @unknown default:
                        print("ReachabilityService: unsatisfiedReason: default")
                    }
                } else {
                    // Fallback on earlier versions
                }

                self?.isNetworkAvailable = false
                break
            case .requiresConnection:
                print("ReachabilityService: requiresConnection")
                self?.isNetworkAvailable = false
                break
            @unknown default:
                print("ReachabilityService: default")
                self?.isNetworkAvailable = false
            }
            if path.usesInterfaceType(.wifi) {
                self?.typeOfCurrentConnection = .wifi
            } else if path.usesInterfaceType(.cellular) {
                self?.typeOfCurrentConnection = .cellular
            } else if path.usesInterfaceType(.loopback) {
                self?.typeOfCurrentConnection = .loopBack
            } else if path.usesInterfaceType(.wiredEthernet) {
                self?.typeOfCurrentConnection = .wired
            } else if path.usesInterfaceType(.other) {
                self?.typeOfCurrentConnection = .other
            }
        }

        monitor.start(queue: backgroundQueue)
    }
}

Usage:

In your view model:

private let reachability = ReachabilityService()

init() {
    reachability.$isNetworkAvailable.sink { [weak self] isConnected in
        self?.isConnected = isConnected ?? false
    }.store(in: &cancelBag)
}

In your controller:

viewModel.$isConnected.sink { [weak self] isConnected in
    print("isConnected: \(isConnected)")
    DispatchQueue.main.async {
        //Update your UI in here
    }
}.store(in: &bindings)
Olcay Ertaş
  • 5,374
  • 8
  • 71
  • 98
0

Many of these answers did not work anymore.

The reason was the use of vpn (because of corona, we now test over vpn instead of the companies wifi)

using apple's Network framework and based on code found here https://medium.com/@udaykiran.munaga/swift-check-for-internet-connectivity-14e355fa10c5 I was able to detect wifi and cellular each separate. The path in general stays satisfied due to the use of the vpn and therefore would always return true for isConnectedToNetwork().

Below the code that uses the apple Network framework, but rewritten so that it still uses Reachability.isConnectedToNetwork() in your existing code.

    import Network

class Reachability {
    static let shared = Reachability()

    let monitorForWifi = NWPathMonitor(requiredInterfaceType: .wifi)
    let monitorForCellular = NWPathMonitor(requiredInterfaceType: .cellular)
    private var wifiStatus: NWPath.Status = .requiresConnection
    private var cellularStatus: NWPath.Status = .requiresConnection
    var isReachable: Bool { wifiStatus == .satisfied || isReachableOnCellular }
    var isReachableOnCellular: Bool { cellularStatus == .satisfied }

    func startMonitoring() {
        monitorForWifi.pathUpdateHandler = { [weak self] path in
            self?.wifiStatus = path.status

            if path.status == .satisfied {
                DLog.message("Wifi is connected!")
                // post connected notification
            } else {
                DLog.message("No wifi connection.")
                // post disconnected notification
            }
        }
        monitorForCellular.pathUpdateHandler = { [weak self] path in
            self?.cellularStatus = path.status

            if path.status == .satisfied {
                DLog.message("Cellular connection is connected!")
                // post connected notification
            } else {
                DLog.message("No cellular connection.")
                // post disconnected notification
            }
        }

        let queue = DispatchQueue(label: "NetworkMonitor")
        monitorForCellular.start(queue: queue)
        monitorForWifi.start(queue: queue)
    }

    func stopMonitoring() {
        monitorForWifi.cancel()
        monitorForCellular.cancel()
    }
    
    class func isConnectedToNetwork() -> Bool {
        return shared.isReachable
    }
}

then in your Appdelegate didFinishLaunchingWithOptions: start monitoring.

Reachability.shared.startMonitoring()
Lex
  • 56
  • 2
-1

This is my version. Essentially it doesn't bring anything new. I bound it to UIDevice.

import UIKit
import SystemConfiguration

extension UIDevice {

    open class var isConnectedToNetwork: Bool {
        get {
            var zeroAddress = sockaddr_in()
            zeroAddress.sin_len = UInt8(MemoryLayout.size(ofValue: zeroAddress))
            zeroAddress.sin_family = sa_family_t(AF_INET)

            guard
                let defaultRouteReachability: SCNetworkReachability = withUnsafePointer(to: &zeroAddress, {
                    $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
                        SCNetworkReachabilityCreateWithAddress(nil, $0)
                    }
                }),
                var flags: SCNetworkReachabilityFlags = SCNetworkReachabilityFlags() as SCNetworkReachabilityFlags?,
                SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags)
                else { return false }

            return flags.contains(.reachable) && !flags.contains(.connectionRequired)
        }
    }

}

print("Network status availability: " + ( UIDevice.isConnectedToNetwork ? "true" : "false" ))
jake1981
  • 193
  • 2
  • 11
-1

here is the same code with accepted answer but I find it more useful for some cases to use closures

import SystemConfiguration

public class Reachability {

    class func isConnectedToNetwork(isConnected : (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(MemoryLayout.size(ofValue: zeroAddress))
        zeroAddress.sin_family = sa_family_t(AF_INET)

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

        var flags: SCNetworkReachabilityFlags = SCNetworkReachabilityFlags(rawValue: 0)
        if SCNetworkReachabilityGetFlags(defaultRouteReachability!, &flags) == false {
            isConnected(false)
        }

        /* Only Working for WIFI
        let isReachable = flags == .reachable
        let needsConnection = flags == .connectionRequired

        return isReachable && !needsConnection
        */

        // Working for Cellular and WIFI
        let isReachable = (flags.rawValue & UInt32(kSCNetworkFlagsReachable)) != 0
        let needsConnection = (flags.rawValue & UInt32(kSCNetworkFlagsConnectionRequired)) != 0
        let ret = (isReachable && !needsConnection)

        isConnected(ret)
    }
}

and here is how to use it:

Reachability.isConnectedToNetwork { (isConnected) in
    if isConnected {
        //We have internet connection | get data from server
    } else {
        //We don't have internet connection | load from database 
    }
}
Sasan Log
  • 409
  • 4
  • 13