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
}
}
}
Related
When I try to connect to Facebook in my iOS application, the app redirects me to the Facebook app and then there is a loop between the two apps, meaning that the user is being sent back and forth between the two apps without being able to complete the login process. I suspect that there is something missing in my code to properly handle the redirect URLs for the PianoID SDK and I would like to know what needs to be done to fix this issue.
this is my appDelegate class :
#objc class AppDelegate: FlutterAppDelegate, PianoIDDelegate {
private var flutterResult: FlutterResult? = nil
#Published private(set) var initialized = false
#Published private(set) var token: PianoIDToken?
var cookiessseion = ""
var startdate = ""
var enddate = ""
var pianoToken=""
func pianoID(_ pianoID: PianoOAuth.PianoID, didSignInForToken token: PianoOAuth.PianoIDToken!, withError error: Error!) {}
func pianoID(_ pianoID: PianoOAuth.PianoID, didSignOutWithError error: Error!) {}
func pianoIDSignInDidCancel(_ pianoID: PianoOAuth.PianoID) {}
override func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
PianoID.shared.endpointUrl = Settings.endpoint.api
PianoID.shared.aid = Settings.aid
PianoID.shared.isSandbox = true
PianoID.shared.signUpEnabled = true
PianoID.shared.googleClientId = Settings.clientId
PianoID.shared.delegate = self
PianoOAuth.PianoIDApplicationDelegate.shared.application(application, didFinishLaunchingWithOptions: launchOptions)
GeneratedPluginRegistrant.register(with: self)
let name = Bundle.main.infoDictionary?["CFBundleName"]
let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"]
GEMConfig.sharedInstance().setAppInfo(name as? String, version: version as? String)
//FirebaseApp.configure()
guard let controller = window?.rootViewController as? FlutterViewController else {
fatalError("rootViewController is not type FlutterViewController")
}
let Channel = FlutterMethodChannel(name: "flutter.native/helper", binaryMessenger: controller.binaryMessenger)
Channel.setMethodCallHandler({
[weak self] (call: FlutterMethodCall, result: #escaping FlutterResult) -> Void in
if(call.method == "testpiano"){
self?.initPianoID()
self?.flutterResult = result
} else {
result(FlutterMethodNotImplemented)
}
})
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
/// Sign In callback
func signIn(result: PianoIDSignInResult!, withError error: Error!) {
if let r = result {
token = r.token
do {
let PianoIDTokenString = try JSONParserSwift.getJSON(object: token)
self.flutterResult?(PianoIDTokenString)
} catch {
print("Unexpected data error: \(error)")
}
} else {
}
}
/// Sign Out callback
func signOut(withError error: Error!) {
token = nil
}
/// Cancel callback
func cancel() {
}
private func initPianoID() {
PianoID.shared.signIn()
}
func openSettings(alert: UIAlertAction!) {
if let url = URL.init(string: UIApplication.openSettingsURLString) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
}
override public func applicationDidBecomeActive(_ application: UIApplication) {
debugPrint("applicationDidBecomeActive")
if #available(iOS 14, *) {
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
ATTrackingManager.requestTrackingAuthorization { status in
}
}
}
}
}
extension Date {
func currentTimeMillis() -> Int64 {
return Int64(self.timeIntervalSince1970)
}
}
And this is the documentation : link
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.
On the networking side, DNS Proxy is one of the biggest features of iOS 11. But they haven't provided much documentation or samples regarding it. There's a talk on it as well where they have just given a description of what is possible with DNS Proxy.
I want to create a working sample of it but didn't get success till now. So I have created a Network Extension with DNS Proxy entitlements and added a DNS Proxy Provider. Here's the code:
class DNSProxyProvider: NEDNSProxyProvider {
let defaults = UserDefaults(suiteName: "group.com.securly.dnsProxy")
override init() {
NSLog("QNEDNSProxy.Provider: init")
super.init()
// +++ might want to set up KVO on `systemDNSSettings`
}
override func startProxy(options:[String: Any]? = nil, completionHandler: #escaping (Error?) -> Void) {
NSLog("QNEDNSProxy.Provider: start")
// self.defaults?.set("DidStart", forKey: "DidStart")
completionHandler(nil)
}
override func stopProxy(with reason: NEProviderStopReason, completionHandler: #escaping () -> Void) {
NSLog("QNEDNSProxy.Provider: stop")
completionHandler()
}
override func handleNewFlow(_ flow: NEAppProxyFlow) -> Bool {
NSLog("QNEDNSProxy.Provider: new flow (denied)")
// self.defaults?.set("DidHandleNewFlow", forKey: "DidHandleNewFlow")
return true
}
}
Then in AppDelegate, I declare a NEDNSProxyManager and use it as:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
let manager = NEDNSProxyManager.shared()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
self.enable()
return true
}
private func enable() {
self.update {
self.manager.localizedDescription = "DNSProxySample"
let proto = NEDNSProxyProviderProtocol()
// proto.providerConfiguration = +++
proto.providerBundleIdentifier = "com.securly.dnsProxy"
self.manager.providerProtocol = proto
self.manager.isEnabled = true
}
}
private func disable() {
self.update {
self.manager.isEnabled = false
}
}
private func update(_ body: #escaping () -> Void) {
self.manager.loadFromPreferences { (error) in
guard error == nil else {
NSLog("DNSProxySample.App: load error")
return
}
body()
self.manager.saveToPreferences { (error) in
guard error == nil else {
NSLog("DNSProxySample.App: save error")
return
}
NSLog("DNSProxySample.App: saved")
}
}
}
}
Questions/Issues:
Why isn't startProxy or handleNewFlow called? Is there anything wrong in the setup?
How do I mention custom DNS address?
I managed to trigger startProxy and handleFlow on DNSProxyProvider by system. My configurations are like this:
Entitlements on app target
Entitlements on DNSProxy Extension
Red line is something similar to: group.com.xzy.project_name
Info.plist file on Extension
AppDelegate
import UIKit
import NetworkExtension
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
let manager = NEDNSProxyManager.shared()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
self.enable()
return true
}
private func enable() {
self.update {
self.manager.localizedDescription = "DNS"
let proto = NEDNSProxyProviderProtocol()
proto.providerBundleIdentifier = "EXTENSION_BUNDLE_IDENTIFIER_WHICH_HAS_DNS_PROXY"
self.manager.providerProtocol = proto
self.manager.isEnabled = true
}
}
private func disable() {
self.update {
self.manager.isEnabled = false
}
}
private func update(_ body: #escaping () -> Void) {
self.manager.loadFromPreferences { (error) in
guard error == nil else {
NSLog("DNS Test App: load error")
return
}
body()
self.manager.saveToPreferences { (error) in
guard error == nil else {
NSLog("DNS Test App: save error")
return
}
NSLog("DNS Test App: saved")
}
}
}
}
DO NOT FORGET TO CHANGE BUNDLE IDENTIFIER at here proto.providerBundleIdentifier = "EXTENSION_BUNDLE_IDENTIFIER_WHICH_HAS_DNS_PROXY"
DNSProxyProvider
import NetworkExtension
class DNSProxyProvider: NEDNSProxyProvider {
override init() {
NSLog("DNSProxyProvider: init")
super.init()
}
override func startProxy(options:[String: Any]? = nil, completionHandler: #escaping (Error?) -> Void) {
NSLog("DNSProxyProvider: startProxy")
completionHandler(nil)
}
override func stopProxy(with reason: NEProviderStopReason, completionHandler: #escaping () -> Void) {
NSLog("DNSProxyProvider: stopProxy")
completionHandler()
}
override func sleep(completionHandler: #escaping () -> Void) {
NSLog("DNSProxyProvider: sleep")
completionHandler()
}
override func wake() {
NSLog("DNSProxyProvider: wake")
}
override func handleNewFlow(_ flow: NEAppProxyFlow) -> Bool {
NSLog("DNSProxyProvider: handleFlow")
if let tcpFlow = flow as? NEAppProxyTCPFlow {
let remoteHost = (tcpFlow.remoteEndpoint as! NWHostEndpoint).hostname
let remotePort = (tcpFlow.remoteEndpoint as! NWHostEndpoint).port
NSLog("DNSProxyProvider TCP HOST : \(remoteHost)")
NSLog("DNSProxyProvider TCP PORT : \(remotePort)")
} else if let udpFlow = flow as? NEAppProxyUDPFlow {
let localHost = (udpFlow.localEndpoint as! NWHostEndpoint).hostname
let localPort = (udpFlow.localEndpoint as! NWHostEndpoint).port
NSLog("DNSProxyProvider UDP HOST : \(localHost)")
NSLog("DNSProxyProvider UDP PORT : \(localPort)")
}
return true
}
}
As a last step run the app on a real iOS Device.
If you want to display extension logs open Console.app from your Mac.
To debug the extension: Your main app should be selected from run menu. Select Attach to Process by PID or Name... from Xcode's Debug menu and then type your extension's name, press Attach button. After you see the Waiting to attach to EXTENSION_NAME on XYZ's iPhone. Run your app target on a iOS device.
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.
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.