Creating an easy to use extension/function for Reachability (host/internet WIFI, Cellular) - ios

I am working on a complex app and I want to test both host in internet reachability on each ViewController that receive data from server I am currently using this library for Reachability
https://github.com/ashleymills/Reachability.swift
And I want to create an easy method or extension that check the reachability for both internet and host out of this
I have already use the sample code from the library which is below: `import UIKit
import Reachability
class VC22: UIViewController {
#IBOutlet weak var networkStatus: UILabel!
#IBOutlet weak var hostNameLabel: UILabel!
var reachability: Reachability?
override func viewDidLoad() {
super.viewDidLoad()
// Start reachability without a hostname intially
setupReachability(nil, useClosures: true)
startNotifier()
// After 5 seconds, stop and re-start reachability, this time using a hostname
let dispatchTime = DispatchTime.now() + DispatchTimeInterval.seconds(5)
DispatchQueue.main.asyncAfter(deadline: dispatchTime) {
self.stopNotifier()
self.setupReachability("http://81.28.42.42:4242/", useClosures: true)
self.startNotifier()
let dispatchTime = DispatchTime.now() + DispatchTimeInterval.seconds(5)
DispatchQueue.main.asyncAfter(deadline: dispatchTime) {
self.stopNotifier()
self.setupReachability("invalidhost", useClosures: true)
self.startNotifier() }
}
}
func setupReachability(_ hostName: String?, useClosures: Bool) {
hostNameLabel.text = hostName != nil ? hostName : "No host name"
print("--- set up with host name: \(hostNameLabel.text!)")
let reachability = hostName == nil ? Reachability() : Reachability(hostname: hostName!)
self.reachability = reachability
if useClosures {
reachability?.whenReachable = { reachability in
DispatchQueue.main.async {
self.updateLabelColourWhenReachable(reachability)
}
}
reachability?.whenUnreachable = { reachability in
DispatchQueue.main.async {
self.updateLabelColourWhenNotReachable(reachability)
}
}
} else {
NotificationCenter.default.addObserver(self, selector: #selector(VC22.reachabilityChanged(_:)), name: ReachabilityChangedNotification, object: reachability)
}
}
func startNotifier() {
print("--- start notifier")
do {
try reachability?.startNotifier()
} catch {
networkStatus.textColor = .red
networkStatus.text = "Unable to start\nnotifier"
return
}
}
func stopNotifier() {
print("--- stop notifier")
reachability?.stopNotifier()
NotificationCenter.default.removeObserver(self, name: ReachabilityChangedNotification, object: nil)
reachability = nil
}
func updateLabelColourWhenReachable(_ reachability: Reachability) {
print("\(reachability.description) - \(reachability.currentReachabilityString)")
if reachability.isReachableViaWiFi {
self.networkStatus.textColor = .green
} else {
self.networkStatus.textColor = .blue
}
self.networkStatus.text = reachability.currentReachabilityString
}
func updateLabelColourWhenNotReachable(_ reachability: Reachability) {
print("\(reachability.description) - \(reachability.currentReachabilityString)")
self.networkStatus.textColor = .red
self.networkStatus.text = reachability.currentReachabilityString
}
func reachabilityChanged(_ note: Notification) {
let reachability = note.object as! Reachability
if reachability.isReachable {
updateLabelColourWhenReachable(reachability)
} else {
updateLabelColourWhenNotReachable(reachability)
}
}
deinit {
stopNotifier()
}
}
this works fine but I just need a boolean to tell me if it is connected or not that could be reusable through the app
UPDATE currently I am using below 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
}
let isReachable = flags == .reachable
let needsConnection = flags == .connectionRequired
return isReachable && !needsConnection
}
}
and use in viewControllers like below :
if Reachability.isConnectedToNetwork() == true {
print("Internet connection OK")
JSONParseFunction()
} else {
print("Internet connection FAILED")
let alert = UIAlertView(title: "You are not connect to internet", message: "please check you connectivity", delegate: nil, cancelButtonTitle: "OK")
alert.show()
}
This way I just check for internet I need to check both Host and Internet

Is there a reason for not just dropping this in your AppDelegate and subscribe to the observer there instead of doing it on a specific vc?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
prepareReachabilityObserver()
return true
}
private func prepareReachabilityObserver() {
AppDelegate.reachability.whenUnreachable = { reachability in
DispatchQueue.main.async {
print("Not reachable")
}
}
do {
try AppDelegate.reachability.startNotifier()
} catch {
print("Unable to start notifier")
}
}
On the host verification extension part, I'd probably go with something like:
extension UIViewController {
internal func isReachable() -> Bool {
//this is the function you already have.
}
}
which you can then use in every viewcontroller by doing
self.isReachable() //'self' is a UIViewController in this case.
Other than this, I'm having trouble understanding your question, since you seem to already have this solved.
EDIT: I think I understand your question, now. You want to check both if you're reachable and if the hostname you pass around is also reachable.
I don't think it's the best idea to handle both at the same time, since one is a Reachability issue ('can I get an outgoing connection?') and the other one is a connection issue ('can I get a response from this remote place?' or 'Does this request timeout?').
The way I currently handle it is Reachability in something like the AppDelegate and then handling the timeout on a request-by-request manner (which you can then generalize within the network-stuff scope).
To be more specific:
AppDelegate sets Reachability. Then I have a RequestsManager that handles service calls with a configured timeout.
Then you can do something like:
RequestManager.makeRequest("https://an.endpoint.of.yours",
onSuccess: {},
onFailure: { //Here goes your timeout behaviour.
})
Where do I pass the hostname?
I think that's an unneeded behaviour, to be honest. You don't try to open door and then you open door. You just try and see if you're successful. Here's the same. You try to make a request, does it succeed? Awesome. Does it not? Handle accordingly. What do you (The app) care if the failure was because the endpoint was down or because you don't have a working data plan or network's down? The request times out either way, and that's what you care about.
Again, this is all assuming I actually understood you, which I'm not 100% sure.

Related

Swift Socket Sever disconnected while moving to one view controller to other

I am trying to implement swift socket in my application. I am pretty new to socket programming. I have used
https://github.com/swiftsocket/SwiftSocket
for socket connection. I had create NSObject class for Server Connection.
I want Server connected through out my whole application, But Server is disconnected while I'm moving into another ViewController.
class SocketGLobal : NSObject
{
func Connect()
{
let client = TCPClient(address: "www.apple.com", port: 80)
switch client.connect(timeout: 1) {
case .success:
switch client.send(string: "GET / HTTP/1.0\n\n" ) {
case .success:
guard let data = client.read(1024*10) else { return }
if let response = String(bytes: data, encoding: .utf8) {
print(response)
}
case .failure(let error):
print(error)
}
case .failure(let error):
print(error)
}
}
}
I faced the same problem. So I created the custom ViewController and used GCDAsyncUdpSocket for socket connection. In your pod file add pod 'CocoaAsyncSocket' to use GCDAsyncUdpSocket. Here is the code snippet for custom ViewController.
import CocoaAsyncSocket
class CustomViewController: UIViewController, GCDAsyncUdpSocketDelegate {
//MARK: VARIABLE DECLARATION **********
var _udpSocket: GCDAsyncUdpSocket?
var udpSocket: GCDAsyncUdpSocket? {
get {
if _udpSocket == nil {
let port = 80
let sock = GCDAsyncUdpSocket(delegate: self, delegateQueue: DispatchQueue.main)
do {
sock.setIPv6Enabled(false)
// sock?.setIPv4Enabled(true)
// sock?.setPreferIPv4()
try sock.enableBroadcast(true)
try sock.bind(toPort: UInt16(port))
try sock.beginReceiving()
} catch let err as NSError {
print(">>> Error while initializing socket: \(err.localizedDescription)")
sock.close()
return nil
}
_udpSocket = sock
}
return _udpSocket
}
set {
_udpSocket?.close()
_udpSocket = newValue
}
}
//MARK: UDP SERVICES *********
//Start UDP Receiver
func startUDPService(){
guard udpSocket != nil else {
return
}
}
//Stop UDP Receiver
func closeUDPService(){
if udpSocket != nil {
udpSocket?.close()
udpSocket = nil
}
}
//MARK: HANDLING OF RECEIVED DATA **********
func udpSocket(_ sock: GCDAsyncUdpSocket, didReceive data: Data, fromAddress address: Data, withFilterContext filterContext: Any?) {
var hostPtr : NSString? = nil
GCDAsyncUdpSocket.getHost(&hostPtr, port: &send_port, fromAddress: address)
var receivedText = ""
if let stringData = String(data: data as Data, encoding: String.Encoding.utf8) {
receivedText = stringData
}
print("Data received: \(String(describing: hostPtr)), \(receivedText)")
}
}
In your each ViewController implement as follows:
import UIKit
class YourViewController: CustomViewController {
//MARK: LIFE CYCLE METHODS **********
override func viewWillAppear(_ animated: Bool) {
//MARK: START UDP SERVICE
self.startUDPService()
}
override func viewWillDisappear(_ animated: Bool) {
//MARK: STOP UDP SERVICE
self.closeUDPService()
}
}
I hope this will help you.

I want logout a user after 1 hour once they go offline if they come online withn that 1 hour it should stop

I tried saving my time in UserDefaults and adding 1 hour after once they go offline. I tried this too:
if isNetworkAvailable {
print(isNetworkAvailable)
}
else {
DispatchQueue.main.asyncAfter(deadline: .now() + 10.0, execute: {
self.logginout()
})
}
I'm checking my Reachibility through Reachibility pod I have written a network manager like this:
import UIKit
class NetworkManager: NSObject {
var reachability: Reachability!
// Create a singleton instance
static let sharedInstance: NetworkManager = { return NetworkManager() }()
override init() {
super.init()
// Initialise reachability
reachability = Reachability()!
// Register an observer for the network status
NotificationCenter.default.addObserver(
self,
selector: #selector(networkStatusChanged(_:)),
name: .reachabilityChanged,
object: reachability
)
do {
// Start the network status notifier
try reachability.startNotifier()
} catch {
print("Unable to start notifier")
}
}
#objc func networkStatusChanged(_ notification: Notification) {
// Do something globally here!
}
static func stopNotifier() -> Void {
do {
// Stop the network status notifier
try (NetworkManager.sharedInstance.reachability).startNotifier()
} catch {
print("Error stopping notifier")
}
}
// Network is reachable
static func isReachable(completed: #escaping (NetworkManager) -> Void) {
if (NetworkManager.sharedInstance.reachability).connection != .none {
completed(NetworkManager.sharedInstance)
}
}
// Network is unreachable
static func isUnreachable(completed: #escaping (NetworkManager) -> Void) {
if (NetworkManager.sharedInstance.reachability).connection == .none {
completed(NetworkManager.sharedInstance)
}
}
// Network is reachable via WWAN/Cellular
static func isReachableViaWWAN(completed: #escaping (NetworkManager) -> Void) {
if (NetworkManager.sharedInstance.reachability).connection == .cellular {
completed(NetworkManager.sharedInstance)
}
}
// Network is reachable via WiFi
static func isReachableViaWiFi(completed: #escaping (NetworkManager) -> Void) {
if (NetworkManager.sharedInstance.reachability).connection == .wifi {
completed(NetworkManager.sharedInstance)
}
}
}
And in delegate I'm checking it like this:
network.reachability.whenUnreachable = { unreach in
isNetworkAvailable = false
let banner = NotificationBanner(title: "Your offline", subtitle: "Please Connect to internet", style: .danger)
banner.onSwipeUp = {
banner.dismiss()
}
print(time)
banner.show()
}
Is there a way to achieve this? I have written this block inside didLaunchWithOptions.

How can we use Alamofire NetworkReachabilityManager in a singleton?

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

how to block user , if he/she not connected to internet connection when my app load

I have one app, which is fully map view.What I need is I need to show some alert if user not connected to internet . I have done that. Here is the code :
Reachability.swift.
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
}
}
if Reachability.isConnectedToNetwork() == true {
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()
}
But what I need is : when user not connected to internet , one UIAlertView message will show. in that alert I have one OK button. So till user connect to internet , it is possible to redirect them to mobile data on/off settings or it is possible to show UIAlertView message till the user connect to internet..
Please help me out !!
On click of ok button in alertview you can redirect the user to desired page, calling the appropriate segue. second option if you want to block the user, just show an activity indicator with the custom message.
One thing is letting users know about the network status.
Having your app built to support all of these network states is completely different thing.
Make sure your users are aware of the current network status but also make sure your application behaves accordingly to it.
And no, you cannot redirect directly to Wi-Fi Settings.
Launch iOS Device Settings page
Step.1 Go to Project settings --> Info --> URL Types --> Add New URL Schemes
Step.2 Check for internet in viewWillAppear method and show the alert
override func viewWillAppear(animated: Bool)
{
//check for internet
if Reachability.isConnectedToNetwork() == true
{
print("Internet connection OK")
}
else
{
print("Internet connection off")
let alertView: UIAlertView = UIAlertView(title: "Alert!", message: "Please enable internet settings", delegate: self, cancelButtonTitle: "Settings", otherButtonTitles: "Cancel")
alertView.show()
return
}
}
Step.3 Handle alert button click
func alertView(alertView: UIAlertView, clickedButtonAtIndex buttonIndex: Int)
{
if buttonIndex == 0
{
//This will open ios devices wifi settings
UIApplication.sharedApplication().openURL(NSURL(string: "prefs:root")!)
}
else if buttonIndex == 1
{
//TODO for cancel
}
}
Note: Do not forget to confirm UIAlertViewDelegate
See here full example code I have tested
import UIKit
class ViewController: UIViewController, UIAlertViewDelegate
{
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func viewWillAppear(animated: Bool)
{
//check for internet
if Reachability.isConnectedToNetwork() == true
{
print("Internet connection OK")
}
else
{
print("Internet connection off")
let alertView: UIAlertView = UIAlertView(title: "Alert!", message: "Please enable internet settings", delegate: self, cancelButtonTitle: "Settings", otherButtonTitles: "Cancel")
alertView.show()
return
}
}
func alertView(alertView: UIAlertView, clickedButtonAtIndex buttonIndex: Int)
{
if buttonIndex == 0
{
//This will open ios devices wifi settings
UIApplication.sharedApplication().openURL(NSURL(string: "prefs:root")!)
}
else if buttonIndex == 1
{
//TODO for cancel
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
1-) Your App Target > Info > URL Types then add new URL Types like this;
2-) In your AppDelegate, define this variable;
var isInternetConnected = false
3-) In your UIViewController, define an AppDelegate variable then in UIViewController viewDidLoad() method, start listening connection;
let appDelegate: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "internetNotifier:", name: kReachabilityChangedNotification, object: nil)
Reachability.reachabilityForInternetConnection().startNotifier()
}
4-) In your internetNotifier method, check connection;
func internetNotifier(notification: NSNotification) {
if let reachObject = notification.object as? Reachability {
switch reachObject.isReachable() {
case true:
appDelegate.isInternetConnected = true
hideAlertController()
case false:
appDelegate.isInternetConnected = false
presentViewController(UIAlertController.showAlertController(), animated: true, completion: nil)
}
}
}
func hideAlertController() {
if self.navigationController?.visibleViewController is UIAlertController {
dismissViewControllerAnimated(true, completion: nil)
}
}
5-) Create new Extension of UIAlertController in your AppDelegate;
extension UIAlertController {
class final func showAlertController() -> UIAlertController {
let internetController = self.init(title: "Error", message: "No internet connection. Go to Settings and open your Wi-Fİ", preferredStyle: .Alert)
internetController.addAction(UIAlertAction(title: "Go to Settings", style: .Default, handler: { (internetController) -> Void in
if let settingsURL = NSURL(string: "prefs:root=WIFI") where UIApplication.sharedApplication().canOpenURL(settingsURL) {
dispatch_async(dispatch_get_main_queue()) {
UIApplication.sharedApplication().openURL(settingsURL)
}
}
}))
return internetController
}
}
6-) Last and the most important step is, call your UIAlertController in AppDelegate;
func applicationWillEnterForeground(application: UIApplication) {
if !isInternetConnected {
window?.rootViewController?.presentViewController(UIAlertController.showAlertController(), animated: true, completion: nil)
}
}
Because, if user go settings open network then back your application and still not connection, you need to show him your alert again. Do not forget remove your connection observer in your UIViewController;
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self, name: kReachabilityChangedNotification, object: nil)
Reachability.reachabilityForInternetConnection().stopNotifier()
}

How to get notifications for Network change(Connect,Disconnect,Change)

In my app I have to alert whenever a network change happens for example when a app is connected or disconnected or changed(wifi to data) network.
But finding it by reaching a url seems expensive.
Is there any public api available in iOS to do this.
I followed this tutorial
https://www.youtube.com/watch?v=wDZmz9IsB-8
credit --> Vasil Nunev
Download this class and import to my project --> Reachability.swift
https://github.com/ashleymills/Reachability.swift
(Reachability.swift) --> Unzip -->Sources/Reachability.swift
This is my view controller
import UIKit
class ViewController: UIViewController {
let reachability = Reachability()!
override func viewDidLoad() {
super.viewDidLoad()
reachability.whenReachable = { _ in
DispatchQueue.main.async {
self.view.backgroundColor = UIColor.green
}
}
reachability.whenUnreachable = { _ in
DispatchQueue.main.async {
self.view.backgroundColor = UIColor.red
}
}
NotificationCenter.default.addObserver(self, selector: #selector(internetChanged), name: Notification.Name.reachabilityChanged , object: reachability)
do{
try reachability.startNotifier()
} catch {
print("Could not strat notifier")
}
}
#objc func internetChanged(note:Notification) {
let reachability = note.object as! Reachability
if reachability.isReachable{
if reachability.isReachableViaWiFi{
self.view.backgroundColor = UIColor.green
}
else{
DispatchQueue.main.async {
self.view.backgroundColor = UIColor.orange
}
}
} else{
DispatchQueue.main.async {
self.view.backgroundColor = UIColor.red
}
}
}
}
Look at this official Apple documentation
Using Reachability you can recognize the type of your connection.
There is also a complete example on how detect the changes and the documentation is updated now for ARC.
I hope this can help you
Possible help:
Reachability
Event notification for network activity
If you're using Alamofire in your application, you can use it's NetworkReachabilityManager.swift class to listen for network's connectivity.
This is the piece of code.
#discardableResult
open func startListening(onQueue queue: DispatchQueue = .main,
onUpdatePerforming listener: #escaping Listener) -> Bool {
stopListening()
$mutableState.write { state in
state.listenerQueue = queue
state.listener = listener
}
var context = SCNetworkReachabilityContext(version: 0,
info: Unmanaged.passUnretained(self).toOpaque(),
retain: nil,
release: nil,
copyDescription: nil)
let callback: SCNetworkReachabilityCallBack = { _, flags, info in
guard let info = info else { return }
let instance = Unmanaged<NetworkReachabilityManager>.fromOpaque(info).takeUnretainedValue()
instance.notifyListener(flags)
}
let queueAdded = SCNetworkReachabilitySetDispatchQueue(reachability, reachabilityQueue)
let callbackAdded = SCNetworkReachabilitySetCallback(reachability, callback, &context)
// Manually call listener to give initial state, since the framework may not.
if let currentFlags = flags {
reachabilityQueue.async {
self.notifyListener(currentFlags)
}
}
return callbackAdded && queueAdded
}
/// Stops listening for changes in network reachability status.
open func stopListening() {
SCNetworkReachabilitySetCallback(reachability, nil, nil)
SCNetworkReachabilitySetDispatchQueue(reachability, nil)
$mutableState.write { state in
state.listener = nil
state.listenerQueue = nil
state.previousStatus = nil
}
}
You can also use Apple's SCNetworkReachbility class instead.

Resources