b3rge
b3rge

Reputation: 5009

Check for internet connection with Swift

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

Upvotes: 318

Views: 361078

Answers (30)

Mannam Brahmaiah
Mannam Brahmaiah

Reputation: 2283

//  IDReachability.swift

import Foundation
import SystemConfiguration

class Reachability {
 static let shared = Reachability()

 private var reachabilityRef: SCNetworkReachability?
 private var isMonitoring = false

 // Notification name for internet status change
 static let reachabilityChangedNotification = Notification.Name("ReachabilityChangedNotification")

 private init() {
    var address = sockaddr_in()
    address.sin_len = UInt8(MemoryLayout<sockaddr_in>.size)
    address.sin_family = sa_family_t(AF_INET)

    reachabilityRef = withUnsafePointer(to: &address, {
        $0.withMemoryRebound(to: sockaddr.self, capacity: 1) { sockaddrPtr in
            SCNetworkReachabilityCreateWithAddress(nil, sockaddrPtr)
        }
    })
}

// Start monitoring network changes
func startMonitoring() {
    guard let reachability = reachabilityRef, !isMonitoring else { return }

    var context = SCNetworkReachabilityContext(
        version: 0,
        info: nil,
        retain: nil,
        release: nil,
        copyDescription: nil
    )

    if SCNetworkReachabilitySetCallback(reachability, { (_, flags, _) in
        let isReachable = flags.contains(.reachable)
        let needsConnection = flags.contains(.connectionRequired)
        let hasInternet = isReachable && !needsConnection

        NotificationCenter.default.post(name: Reachability.reachabilityChangedNotification, object: nil, userInfo: ["isConnected": hasInternet])
    }, &context) {
        SCNetworkReachabilityScheduleWithRunLoop(reachability, CFRunLoopGetCurrent(), CFRunLoopMode.defaultMode.rawValue)
        isMonitoring = true
    }
}

// Stop monitoring network changes
func stopMonitoring() {
    guard let reachability = reachabilityRef, isMonitoring else { return }
    SCNetworkReachabilityUnscheduleFromRunLoop(reachability, CFRunLoopGetCurrent(), CFRunLoopMode.defaultMode.rawValue)
    isMonitoring = false
}

// Check if the internet is available
func isInternetAvailable() -> Bool {
    guard let reachability = reachabilityRef else { return false }

    var flags = SCNetworkReachabilityFlags()
    if SCNetworkReachabilityGetFlags(reachability, &flags) {
        let isReachable = flags.contains(.reachable)
        let needsConnection = flags.contains(.connectionRequired)
        return isReachable && !needsConnection
    }
    return false
 }
}

To Monitor Network

// Start monitoring network changes
  Reachability.shared.startMonitoring()
   
// Listen for notifications
  NotificationCenter.default.addObserver(self, selector: #selector(handleReachabilityChange(_:)), name: Reachability.reachabilityChangedNotification, object: nil)


@objc func handleReachabilityChange(_ notification: Notification) {
    if let userInfo = notification.userInfo, let isConnected = userInfo["isConnected"] as? Bool {
        if isConnected {
            print("Connected to the internet")
        } else {
            print("Disconnected from the internet")
        }
    }
}

Upvotes: 0

Yogendra Singh
Yogendra Singh

Reputation: 2241

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

Upvotes: 31

ytur
ytur

Reputation: 1232

I've made some improvement on Lex's example. I put some extra controls to solve double-trigger problem and also added notification support to listen status changes.

The controls that I've added to prevent the double-trigger problem, also show which connection source is using primarily by the device to internet access.

For example, even if the device is connected to both cellular and Wi-Fi at the same time, the "status" returns as "connectedViaWiFi" to indicating that the current internet access is over Wi-Fi.

import Foundation
import Network

class Reachability {

    enum StatusFlag {
        case unknow
        case noConnection
        case connectedViaWiFi
        case connectedViaCellular
    }

    static let connectionStatusHasChangedNotification = NSNotification.Name("Reachability.connectionStatusHasChangedNotification")
    static let shared = Reachability()

    private var monitorForWifi: NWPathMonitor?
    private var monitorForCellular: NWPathMonitor?
    private var wifiStatus: NWPath.Status = .requiresConnection
    private var cellularStatus: NWPath.Status = .requiresConnection
    private var ignoreInitialWiFiStatusUpdate: Bool = true
    private var ignoreInitialCelluluarStatusUpdate: Bool = true
    private var isReachableOnCellular: Bool { cellularStatus == .satisfied }
    private var isReachableOnWiFi: Bool { wifiStatus == .satisfied }
    var status: StatusFlag = .unknow {
        didSet {
            guard status != oldValue else { return }
            DispatchQueue.main.async { [weak self] in
                NotificationCenter.default.post(name: Self.connectionStatusHasChangedNotification,
                                                object: self?.status)
            }
        }
    }

    func startMonitoring() {
        monitorForWifi = NWPathMonitor(requiredInterfaceType: .wifi)
        monitorForWifi?.pathUpdateHandler = { [weak self] path in
            self?.wifiStatus = path.status
            self?.ignoreInitialWiFiStatusUpdate = false
            self?.updateStatus()
        }
        monitorForCellular = NWPathMonitor(requiredInterfaceType: .cellular)
        monitorForCellular?.pathUpdateHandler = { [weak self] path in
            self?.cellularStatus = path.status
            self?.ignoreInitialCelluluarStatusUpdate = false
            self?.updateStatus()
        }
        let queue = DispatchQueue.global(qos: .background)
        monitorForCellular?.start(queue: queue)
        monitorForWifi?.start(queue: queue)
    }

    func stopMonitoring() {
        monitorForWifi?.cancel()
        monitorForWifi = nil
        monitorForCellular?.cancel()
        monitorForCellular = nil
        wifiStatus = .requiresConnection
        cellularStatus = .requiresConnection
        status = .unknow
        ignoreInitialWiFiStatusUpdate = true
        ignoreInitialCelluluarStatusUpdate = true
    }

    private func updateStatus() {
        if ignoreInitialWiFiStatusUpdate || ignoreInitialCelluluarStatusUpdate {
            return
        }
        if !(isReachableOnCellular && isReachableOnWiFi) {
            if isReachableOnCellular && !isReachableOnWiFi {
                status = .connectedViaCellular
            } else if isReachableOnWiFi && !isReachableOnCellular {
                status = .connectedViaWiFi
            } else {
                status = .noConnection
            }
        } else {
            status = .connectedViaWiFi
        }
    }

    static func isConnectedToNetwork() -> Bool {
        return shared.isReachableOnCellular || shared.isReachableOnWiFi
    }
}

Sample usage

    override func viewDidLoad() {
        super.viewDidLoad()
        NotificationCenter.default.addObserver(self, selector: #selector(reachabilityChanged(_:)), name: Reachability.connectionStatusHasChangedNotification, object: nil)
        Reachability.shared.startMonitoring()
    }

    @objc func reachabilityChanged(_ sender: Notification) {
        guard let statusFlag = sender.object as? Reachability.StatusFlag else { return }
        print("TEST -> statusFlag: \(statusFlag)")
    }

Upvotes: 3

Andy Jazz
Andy Jazz

Reputation: 58563

Xcode 14.0, Swift 5.7

To check for internet connection, use Apple's native Network framework. It has NWPathMonitor class-observer that you can implement to monitor and react to network changes.

import Network

class ViewController: UIViewController {       
    @IBOutlet var label: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.networkMonitoring()
    }
    
    fileprivate func networkMonitoring() {
        let monitor = NWPathMonitor()
        let queue = DispatchQueue(label: "monitoring")
        monitor.start(queue: queue)
        
        monitor.pathUpdateHandler = { path in
            DispatchQueue.main.async {
                switch path.status {
                    case .satisfied:
                        self.label.text = "Intenet connected"
                        self.view.backgroundColor = .systemGreen
                    case .unsatisfied:
                        self.label.text = "No Intenet"
                        self.view.backgroundColor = .systemRed
                    case .requiresConnection:
                        self.label.text = "May be activated"
                        self.view.backgroundColor = .systemBlue
                    @unknown default:  fatalError()
                }
            }
        }
    }
}

Upvotes: 5

dimohamdy
dimohamdy

Reputation: 3053

Swift 5.5, iOS 12+, and no more third-party library.

Please check this gist https://gist.github.com/dimohamdy/5166ba6c88f6954fa6b23bc9f28cbe12

Usage

 startNetworkReachabilityObserver()
 
  if Reachability.shared.isConnected {
       print("no internet connection")   
  }

Code

import Network
import Foundation

class Reachability {

    static var shared = Reachability()
    lazy private var monitor = NWPathMonitor()

    var isConnected: Bool {
        return monitor.currentPath.status == .satisfied
    }

    func startNetworkReachabilityObserver() {
        monitor.pathUpdateHandler = { path in
            if path.status == .satisfied {
                NotificationCenter.default.post(name: Notifications.Reachability.connected.name, object: nil)
            } else if path.status == .unsatisfied {
                NotificationCenter.default.post(name: Notifications.Reachability.notConnected.name, object: nil)
            }
        }
        let queue = DispatchQueue.global(qos: .background)
        monitor.start(queue: queue)
    }
}

Upvotes: 7

Nathan Day
Nathan Day

Reputation: 6047

There is no real way to do this, even APIs that do this will try to connect to a particular service and tell you if it succeed after previously failing, the best approach is to just try to connect to your service and handle the error appropriately, if you want some service that will notify you when you become connected, all that will happens is that it periodically calls to a fixed service until it gets a response and then let you know that it succeed, what if the problem is not the internet itself but the specific service you are trying to connect to.

The best approach to this is to design in you connection methods of handling this itself, you can trigger a retry in some given amount of time depending on your error, maybe the number of failures, you can return an error which you can use to display an error message giving the user a possibility to try again, maybe a combination of the try a few time, then return and error.

Another problem similar to this is sending messages you already have sent but haven't gotten a response to, again its best to just write your app to handle this, ignoring request until the one its waiting for has returned, cancelling waiting request if the request is different, etc

Theses things can be written in pretty generic ways so they can be used in many different aspects of your app or even different projects.

Upvotes: 1

Arjun
Arjun

Reputation: 1598

To check internet at an instance, this could be used, using Swift 5

import Foundation
import Alamofire

struct NetworkState {
    var isInternetAvailable:Bool
    {
        return NetworkReachabilityManager()!.isReachable
    }
}

And use it by:

if (NetworkState().isInternetAvailable) {
        //connected to internet
}

Upvotes: 1

grow4gaurav
grow4gaurav

Reputation: 3305

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

Upvotes: 22

Lex
Lex

Reputation: 164

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

Upvotes: 10

Olcay Ertaş
Olcay Ertaş

Reputation: 6236

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)

Upvotes: 1

Raghib Arshi
Raghib Arshi

Reputation: 765

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

Upvotes: -1

Gurjinder Singh
Gurjinder Singh

Reputation: 10329

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
}

Upvotes: 8

M Mahmud Hasan
M Mahmud Hasan

Reputation: 1173

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

Upvotes: 6

Jamil Hasnine Tamim
Jamil Hasnine Tamim

Reputation: 4448

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

Upvotes: 21

Bobby
Bobby

Reputation: 6255

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
}

Upvotes: 9

Martin
Martin

Reputation: 1143

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.

Upvotes: 5

Sasan Soroush
Sasan Soroush

Reputation: 925

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

Upvotes: -1

skymook
skymook

Reputation: 3686

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

Upvotes: 5

Alvin George
Alvin George

Reputation: 14294

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!

Upvotes: 59

Leo Dabus
Leo Dabus

Reputation: 236548

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

Upvotes: 398

A. L. Strine
A. L. Strine

Reputation: 651

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.

Upvotes: 4

J. Doe
J. Doe

Reputation: 13103

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.

Upvotes: 20

osku
osku

Reputation: 315

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

Upvotes: -1

Jack
Jack

Reputation: 14379

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

Upvotes: 75

Rajamohan S
Rajamohan S

Reputation: 7269

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

Upvotes: 292

Illya Krit
Illya Krit

Reputation: 945

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

Upvotes: 2

Alvin George
Alvin George

Reputation: 14294

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

Upvotes: 16

Pavle Mijatovic
Pavle Mijatovic

Reputation: 783

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
}

Upvotes: 1

Chinmay Balutkar
Chinmay Balutkar

Reputation: 234

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

}

Upvotes: 0

douarbou
douarbou

Reputation: 2283

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
}

Upvotes: 1

Related Questions