0

How to solve this issue in swift. I have to check the network connection. If the internet is not connected to the device I have to show this offlineViewController. if it is connected then it shows Normal screen.

Wings
  • 2,398
  • 23
  • 46
Ram
  • 3
  • 2
  • 8
  • https://stackoverflow.com/a/42710600/6898523 – MAhipal Singh Jul 19 '18 at 12:58
  • Thank you @MAhipalSingh but i want to show this offlineVC when net is not connected. how to present and dismiss this OfflineVC – Ram Jul 19 '18 at 13:17
  • Possible duplicate of [How to check for an active Internet connection on iOS or macOS?](https://stackoverflow.com/questions/1083701/how-to-check-for-an-active-internet-connection-on-ios-or-macos) – Nikunj Kumbhani Mar 08 '19 at 10:06

4 Answers4

1

I am using this class to control network change -> Reachability.swift :

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
    }
    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
    }
    var status: Network.Status {
        return  !isConnectedToNetwork ? .unreachable :
            isReachableViaWiFi    ? .wifi :
            isRunningOnDevice     ? .wwan : .unreachable
    }
    var isRunningOnDevice: Bool = {
        #if (arch(i386) || arch(x86_64)) && os(iOS)
            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, CustomStringConvertible {
        case unreachable, wifi, wwan
        var description: String { return rawValue }
    }
    enum Error: Swift.Error {
        case failedToSetCallout
        case failedToSetDispatchQueue
        case failedToCreateWith(String)
        case failedToInitializeWith(sockaddr_in)
    }
}

And I add this code to AppDelegate.swift :

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

    do {
        Network.reachability = try Reachability(hostname: "www.google.com")
        do {
            try Network.reachability?.start()
        } catch let error as Network.Error {
            print(error)
        } catch {
            print(error)
        }
    } catch {
        print(error)
    }

    return true
}

And I add this code to viewDidLoad() in the class I want to control the network:

NotificationCenter.default.addObserver(self, selector: #selector(statusManager), name: .flagsChanged, object: Network.reachability)

You can make checks in this function when the network changes.

func statusManager(_ notification: NSNotification) {
    guard let status = Network.reachability?.status else { return }
    if status == .wifi || status == .wwan {
        offlineViewController.dismiss(animated: true, completion: nil)
    }

    print("Status:", status)
    print("HostName:", Network.reachability?.hostname ?? "nil")
    print("Reachable:", Network.reachability?.isReachable ?? "nil")
    print("Wifi:", Network.reachability?.isReachableViaWiFi ?? "nil")
}
Hilalkah
  • 945
  • 15
  • 37
0
import Alamofire

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

Usage :

if Connectivity.isConnectedToInternet {
     return true
} else {
     self.setErrorMessage(message: "No internet connection", view: view)
     return false
}
Nitish
  • 13,845
  • 28
  • 135
  • 263
  • Thanku @Nitish, But in else part how to present OfflineViewController – Ram Jul 06 '18 at 11:01
  • I am not sure how you want to do that, but use self.present(offlineVC, animated: true, completion: nil) – Nitish Jul 06 '18 at 11:03
  • Yeah @Nitish, i present offline screen in else part but when the internet is connected to the device the offline screen is not dismissing – Ram Jul 06 '18 at 12:13
0

NetworkManager Class

class NetworkManager {

//shared instance
static let shared = NetworkManager()

let reachabilityManager = Alamofire.NetworkReachabilityManager(host: "www.google.com")
let offlineViewController = OfflineViewController()
let currentViewController: UIViewController?

func startNetworkReachabilityObserver() {

    reachabilityManager?.listener = { status in
        switch status {

            case .notReachable:
                offlineViewController.dismiss(animated: false, completion: nil)
                currentViewController.present(offlineViewController, animated: true, completion: nil)
                print("The network is not reachable")

            case .unknown :
                print("It is unknown whether the network is reachable")

            case .reachable(.ethernetOrWiFi):
                offlineViewController.dismiss(animated: true, completion: nil)
                print("The network is reachable over the WiFi connection")

            case .reachable(.wwan):
                offlineViewController.dismiss(animated: true, completion: nil)
                print("The network is reachable over the WWAN connection")

            }
        }

        // start listening
        reachabilityManager?.startListening()
   }
}

Start Network Reachability Observer

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

        // add network reachability observer on app start
        NetworkManager.shared.startNetworkReachabilityObserver()

        return true
    }
}

On each ViewController need to detect network

override func viewDidLoad() {
    super.viewDidLoad()
    NetworkManager.shared.currentViewController = self
}
KTang
  • 340
  • 1
  • 17
  • Hi @KTang its working fine but one problem is coming. when no internet the offline screen is present but when the internet is came the offlineviewcontroller is not dismissing – Ram Jul 06 '18 at 11:22
  • Have you try to put a breakpoint on the cases of switch in network manager? To see the offlineViewController.dismiss() has run or not – KTang Jul 06 '18 at 17:36
  • Yeah I kept breakpoints and i tried, the offlineViewController coming once when network is not connected, if network is connected the offlineViewController is not dismissing, The NetworkManager class is calling but OfflineViewController is showing, its not dismissing. – Ram Jul 07 '18 at 09:02
  • try to add "NetworkManager.shared.startNetworkReachabilityObserver()" in the viewWillAppear of OfflineViewController – KTang Jul 09 '18 at 02:56
  • Try check offlineViewController's instance still alive or not change when network become reachable. – KTang Jul 09 '18 at 07:55
0

Here I use Alamofire version 5.2

NetworkReachabilityManager handler

import Alamofire

class NetworkReachability{

 let sceneDelegate: SceneDelegate = {
    let scene = UIApplication.shared.connectedScenes.first
    let sd = scene?.delegate as? SceneDelegate
    return sd ?? SceneDelegate()
 }()

 var manager:NetworkReachabilityManager?

 init(
    manager:NetworkReachabilityManager? = NetworkReachabilityManager()
 ) {
    self.manager = manager
    self.managerHandler()
 }

 private func managerHandler(){
    self.manager?.startListening(onUpdatePerforming: { (status) in
        switch status{
        
        case .unknown:
            self.sceneDelegate.showOfflineViewController()
        case .notReachable:
            print("Not Reachable")
            self.sceneDelegate.showOfflineViewController()
        case .reachable(.cellular),.reachable(.ethernetOrWiFi):
            print("Reachable")
            self.sceneDelegate.hideOfflineViewController()
        }
    })
  }
}

SceneDelegate

import UIKit

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

 var window: UIWindow?
 var offlineWindow:UIWindow?
 private var networkReachability:NetworkReachability?

 func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    guard let _ = (scene as? UIWindowScene) else { return }
    self.networkReachability = NetworkReachability()
  }
}

extension SceneDelegate{
 func showOfflineViewController(){
    guard let offlineWindow = window?.windowScene else { return }
    
    let offlineViewController = OfflineViewController
    
    self.offlineWindow = UIWindow(windowScene: offlineWindow)
    self.offlineWindow?.rootViewController = offlineViewController
    self.offlineWindow?.makeKeyAndVisible()
}

func hideOfflineViewController(){
    UIView.animate(withDuration: 0.3) {
        self.offlineWindow?.alpha = 0
    } completion: { (_) in
        self.offlineWindow?.isHidden = true
        self.offlineWindow = nil
    }
  }
}
Alvin Butani
  • 51
  • 1
  • 3