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

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.

Related

Why is my posted notification not being received in my Swift iOS app

I have three custom swift classes in my app. One is a NetworkServices class and the other two are custom UIViewController classes. The NetworkServices class posts a notification to the other two classes when wifi is turned on and there is a network connection and also when wifi is turned off and there is no network connection. Currently, the app starts up posting the isConnected notification and code executes as expected. I then turn off wifi and isDisconnected is posted and code executes as expected. However, when I then turn wifi back on again the isConnected post is sent but it is not received by either of the two custom UIViewController classes. My problem is why not?
here are relevant lines of code in my NetworkServices class;
protocol NetworkServicesDelegate: AnyObject {
func sendStockInfo(stocksInfo: [String: StockInfo])
}
final class NetworkServices {
static let sharedInstance = NetworkServices()
...
public func startMonitoring() {
monitor.start(queue: queue)
self.monitor.pathUpdateHandler = { [weak self] path in
if path.status == .satisfied {
// connect the socket
self?.socket.connect()
print("DEBUG: self?.socket.connect() called")
} else {
// disconnect the socket
self?.socket.disconnect()
// print("DEBUG: self?.socket.disconnect() called")
self?.isConnected = false
// post notification that socket is now disconnected
DispatchQueue.main.async {
print("DEBUG: Notification.Name(rawValue) called")
let name = Notification.Name(rawValue: isDisconnectedNotificationKey)
NotificationCenter.default.post(name: name, object: nil)
}
}
}
}
...
}
extension NetworkServices: WebSocketDelegate {
func didReceive(event: WebSocketEvent, client: WebSocket) {
switch event {
case .connected(_):
self.isConnected = true
// post notification that socket is now connected
let name = Notification.Name(rawValue: isConnectedNotificationKey)
NotificationCenter.default.post(name: name, object: nil)
print("DEBUG: Notification isConnected posted")
case .disconnected(let reason, let code):
print("DEBUG: Got disconnected reason = \(reason) code = \(code)")
self.isConnected = false
case .cancelled:
print("DEBUG: cancelled.")
// reconnect socket
socket.connect()
self.isConnected = true
case .reconnectSuggested(let suggestReconnect):
print("DEBUG: suggestReconnect = \(suggestReconnect)")
// post notification that socket is not connected
case .viabilityChanged(let viabilityChanged):
print("DEBUG: viabilityChanged = \(viabilityChanged)")
case .error(let error):
print("DEBUG error: \(String(describing: error?.localizedDescription))")
case .text(let socketString):
// print("DEBUG: .text available")
parseJSONSocketData(socketString)
default:
break
}
}
}
Here are the relevant lines of code from my two custom UIViewController classes;
protocol CompanyPriceListDelegate: AnyObject {
func sendPriceHistory(_ priceHistory: PriceHistory)
}
class CompanyPriceListVC: UITableViewController {
...
let isConnected = Notification.Name(rawValue: isConnectedNotificationKey)
let isNotConnected = Notification.Name(rawValue: isDisconnectedNotificationKey)
override func viewDidLoad() {
super.viewDidLoad()
...
createObservers()
...
}
deinit {
NotificationCenter.default.removeObserver(self)
}
private func createObservers() {
print("DEBUG: createObservers() called")
NotificationCenter.default.addObserver(self, selector: #selector(CompanyPriceListVC.dismissAndFetchStockInfo), name: isConnected, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(CompanyPriceListVC.notifyUserOnScreen(notification:)), name: isNotConnected, object: nil)
}
#objc private func fetchStockInfo() {
NetworkServices.sharedInstance.fetchStockInfo(symbols: companyStockSymbols, delegate: self)
}
#objc private func notifyUserOnScreen(notification: NSNotification) {
self.present(alertController, animated: true)
}
#objc public func dismissAndFetchStockInfo() {
if presentedViewController == alertController {
self.alertController.dismiss(animated: true, completion: nil)
}
fetchStockInfo()
}
...
}
//
// CompanyDetailsVC.swift
// BetVictorTask
//
// Created by Stephen Learmonth on 02/03/2022.
//
import UIKit
class CompanyDetailsVC: UIViewController {
...
let isConnected = Notification.Name(rawValue: isConnectedNotificationKey)
let isNotConnected = Notification.Name(rawValue: isDisconnectedNotificationKey)
override func viewDidLoad() {
super.viewDidLoad()
configureNavigationBar()
addSubViews()
activateConstraints()
createObservers()
fetchCompanyDetails(symbol: symbol)
let controller = navigationController?.viewControllers.first as! CompanyPriceListVC
controller.delegate = self
}
deinit {
NotificationCenter.default.removeObserver(self)
}
private func createObservers() {
NotificationCenter.default.addObserver(self, selector: #selector(CompanyDetailsVC.dismissAndFetchCompanyDetails), name: isConnected, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(CompanyDetailsVC.notifyUserOnScreen(notification:)), name: isNotConnected, object: nil)
}
#objc private func dismissAndFetchCompanyDetails() {
print("DEBUG: dismissAndFetchCompanyDetails() called")
if presentedViewController == alertController {
self.alertController.dismiss(animated: true, completion: nil)
}
fetchCompanyDetails(symbol: symbol)
}
#objc private func notifyUserOnScreen(notification: NSNotification) {
print("DEBUG: notifyUserOnScreen(notification) called")
self.present(alertController, animated: true)
}
...
}
extension CompanyDetailsVC: CompanyPriceListDelegate {
func sendPriceHistory(_ priceHistory: PriceHistory) {
self.updatePrices(priceHistory)
}
}
I’m testing on a real device.
Removed socket.connect() from the.cancelled case

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.

I can't play music with Spotify iOS SDK with Swift 4.0

I have a struggle with Spotify SDK, I followed every step correctly, but I can't play music with my premium account on my project. There is no error or crash, my app directs me to the Spotify login page and after facebook login, It brings me back to my app again. Yesterday I'd get "logged in" print but today I can't. I'm trying to play a song after login and also manually as you see below. I'm wondering, am I lucky to find an answer?
override func viewDidLoad() {
super.viewDidLoad()
self.spotify()
NotificationCenter.default.addObserver(self, selector: #selector(GeneralNewsViewController.updateAfterFirstLogin), name: NSNotification.Name(rawValue: "SpotifySession"), object: nil)
}
func spotify() {
// insert redirect your url and client ID below
auth.redirectURL = URL(string: "correctCallbackURl")
auth.clientID = "correctClientID"
auth.requestedScopes = [SPTAuthStreamingScope, SPTAuthPlaylistReadPrivateScope, SPTAuthPlaylistModifyPublicScope, SPTAuthPlaylistModifyPrivateScope]
loginUrl = auth.spotifyWebAuthenticationURL()
}
func initializaPlayer(authSession:SPTSession){
if self.player == nil {
self.player = SPTAudioStreamingController.sharedInstance()
self.player!.playbackDelegate = self
self.player!.delegate = self
try! player?.start(withClientId: auth.clientID)
self.player!.login(withAccessToken: authSession.accessToken)
}
}
#objc func updateAfterFirstLogin () {
loginButton.isHidden = true
let userDefaults = UserDefaults.standard
if let sessionObj:AnyObject = userDefaults.object(forKey: "SpotifySession") as AnyObject? {
let sessionDataObj = sessionObj as! Data
let firstTimeSession = NSKeyedUnarchiver.unarchiveObject(with: sessionDataObj) as! SPTSession
self.session = firstTimeSession
initializaPlayer(authSession: session)
self.loginButton.isHidden = true
// self.loadingLabel.isHidden = false
}
}
func audioStreamingDidLogin(_ audioStreaming: SPTAudioStreamingController!) {
// after a user authenticates a session, the SPTAudioStreamingController is then initialized and this method called
print("logged in")
self.player?.playSpotifyURI("spotify:track:4aDLPXlzHZm26GppvRwms8", startingWith: 0, startingWithPosition: 0, callback: { (error) in
if (error != nil) {
print("playing!")
} else {
print(error?.localizedDescription)
}
})
}
#objc func play() {
player?.playSpotifyURI("spotify:track:4aDLPXlzHZm26GppvRwms8", startingWith: 0, startingWithPosition: 0, callback: { (err) in
if err != nil {
print(err?.localizedDescription)
} else {
}
})
}
class AppDelegate: UIResponder, UIApplicationDelegate ,UNUserNotificationCenterDelegate{
auth.redirectURL = URL(string: "correctCallbackURl")
auth.sessionUserDefaultsKey = "current session"}
func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
// 2- check if app can handle redirect URL
if auth.canHandle(auth.redirectURL) {
// 3 - handle callback in closure
auth.handleAuthCallback(withTriggeredAuthURL: url, callback: { (error, session) in
// 4- handle error
if error != nil {
print("error!")
}
// 5- Add session to User Defaults
let userDefaults = UserDefaults.standard
let sessionData = NSKeyedArchiver.archivedData(withRootObject: session!)
userDefaults.set(sessionData, forKey: "SpotifySession")
userDefaults.synchronize()
// 6 - Tell notification center login is successful
NotificationCenter.default.post(name: Notification.Name(rawValue: "loginSuccessfull"), object: nil)
})
return true
}
return false
}}
It looks like you're subscribed to the wrong notification, you're subscribing to "SpotifySession" but posting "loginSuccessfull"
this answer will help you making this kind of mistake in the future by having enums for events in a simple way.
good debugging is also key, you could've probably solved this problem with some breakpoints to see where you went wrong.
cheers

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

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.

How to detect if iPhone JUST connected to internet?

I need to make some things in my app, once device is connected to wifi. It is not about "does it connected to wifi?", instead "tell me when device connected to wifi, while it was offline before?"
In other words I need to response on event when iPhone just connected to internet with any way.
Is it possible with AFNetworking or maybe custom API?
You can use Reachability.
In the sample code you will find:
import UIKit
let useClosures = false
class ViewController: UIViewController {
#IBOutlet weak var networkStatus: UILabel!
let reachability = Reachability.reachabilityForInternetConnection()
override func viewDidLoad() {
super.viewDidLoad()
if (useClosures) {
reachability?.whenReachable = { reachability in
self.updateLabelColourWhenReachable(reachability)
}
reachability?.whenUnreachable = { reachability in
self.updateLabelColourWhenNotReachable(reachability)
}
} else {
NSNotificationCenter.defaultCenter().addObserver(self, selector: "reachabilityChanged:", name: ReachabilityChangedNotification, object: reachability)
}
reachability?.startNotifier()
// Initial reachability check
if let reachability = reachability {
if reachability.isReachable() {
updateLabelColourWhenReachable(reachability)
} else {
updateLabelColourWhenNotReachable(reachability)
}
}
}
deinit {
reachability?.stopNotifier()
if (!useClosures) {
NSNotificationCenter.defaultCenter().removeObserver(self, name: ReachabilityChangedNotification, object: nil)
}
}
func updateLabelColourWhenReachable(reachability: Reachability) {
if reachability.isReachableViaWiFi() {
self.networkStatus.textColor = UIColor.greenColor()
} else {
self.networkStatus.textColor = UIColor.blueColor()
}
self.networkStatus.text = reachability.currentReachabilityString
}
func updateLabelColourWhenNotReachable(reachability: Reachability) {
self.networkStatus.textColor = UIColor.redColor()
self.networkStatus.text = reachability.currentReachabilityString
}
func reachabilityChanged(note: NSNotification) {
let reachability = note.object as! Reachability
if reachability.isReachable() {
updateLabelColourWhenReachable(reachability)
} else {
updateLabelColourWhenNotReachable(reachability)
}
}
}
Simple usage for AFNetworking:
Add observer:
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("networkDidChangeStatus"), name: AFNetworkingReachabilityDidChangeNotification, object: nil)
Implement custom Selector:
func networkDidChangeStatus() {
if AFNetworkReachabilityManager.sharedManager().reachable {
print("connected")
} else {
print("disconnected")
}
}
Start monitoring:
AFNetworkReachabilityManager.sharedManager().startMonitoring()

Resources