Alamofire - detect internet connection - ios

How to detect connection to internet?
I have a viewController that displays data fetched from a server. It is presented modally.
I display a message asking for an internet connection if a user is not connected to the internet. Otherwise, I show the data.
When a user connects to the internet without leaving the app, how to detect it? I assume it is possible to do it in Alamofire.
I have used the following code in viewDidLoad and viewWillAppear, but the listener is not called - If I connect & disconnect to WIFI, startListening is not called.
let network = NetworkReachabilityManager()
network!.startListening { status in
if status == .reachable(.cellular) || status == .reachable(.ethernetOrWiFi) {
self.fetchProducts()
} else {
self.noInternetConnnection()
}
}
FYI, you can unable both WIFI and Mobile Data in control center.

If you're declaring network inside a function it's probably being deallocated as soon as the scope ends.
Make sure you store it in an instance variable.

Related

How can we check whether connected network connection is reachable or not in iOS Swift? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
This post was edited and submitted for review 1 year ago and failed to reopen the post:
Original close reason(s) were not resolved
Improve this question
My app supports iOS12 or later and i am using latest macOS and xcode. I did several research on this topic but couldn't found any proper solutions. Yes, i know how to check whether we are connected to the network or not and can get connection types too. But the problem is: is my connected network reachable? I am communicating with an IoT device, and i have to get some data from it, also send it to different server. That device always gives me the data because it is an intranet, as i am connected to it's hotspot. I have to check whether it can send data further on it's own to another remote server(sometimes network connection doesn't work) or if not i need to switch wifi connection to my local network. I have tried several ways but couldn't get satisfactory results.
What #Joakim Danielson said above is what you should be doing. No matter what source you use, it will always give you false information at some point (about being connected to internet or not).
Make the api call.
Check for errors - is it a temporary network error?
Make the call on whether you should retry or show something else to user.
How to check if it might be a temporary network error?
You can use this extension on NSError instance that was returned from api call.
public extension NSError {
var isConnectionAborted: Bool {
return (self.domain == NSPOSIXErrorDomain && self.code == 53)
}
var isTemporaryNetworkError: Bool {
let temporaryNetworkErrorCodes = [
NSURLErrorTimedOut,
NSURLErrorCannotFindHost,
NSURLErrorCannotConnectToHost,
NSURLErrorNetworkConnectionLost,
NSURLErrorDNSLookupFailed,
NSURLErrorHTTPTooManyRedirects,
NSURLErrorResourceUnavailable,
NSURLErrorNotConnectedToInternet,
NSURLErrorRedirectToNonExistentLocation,
NSURLErrorSecureConnectionFailed,
NSURLErrorCannotLoadFromNetwork,
NSURLErrorRequestBodyStreamExhausted,
]
return (self.isConnectionAborted || temporaryNetworkErrorCodes.contains(self.code))
}
var isCancelled: Bool {
return (self.domain == NSURLErrorDomain && self.code == NSURLErrorCancelled)
}
}
I tried an api call, it failed, now I want to query whether I'm connected to ineternet or not. How to know?
Somewhere in your API code, you can add an observer for possible network changes by using NWPathMonitor like this - requires iOS 12
import Network
let monitor = NWPathMonitor()
monitor.start(queue: DispatchQueue(label: "NetworkMonitor"))
monitor.pathUpdateHandler = { (path) in
if path.status == .satisfied {
print("Connected")
} else {
print("Not Connected")
}
}
This allows more granular control as well .cellular, .wifi & .wiredEthernet etc. -
let cellMonitor = NWPathMonitor(requiredInterfaceType: .cellular)
If you still support iOS 11, you can use a 3rd party library like - Reachability
Best practice is to just try: invoke the network request and check the result. What you do if you got an error depends on your use case, if there is a user involved or if the request was made in the background.
For example, you may just output an error and let the user proceed in the app. This may require to design your screen accordingly, i.e. provide a way where the user can retry it (in the screen where you show the content, show an empty content screen and provide a pull-to-refresh or a "retry" button, etc.).
Or, you may analyse the kind of error (URL error or response error) and may apply several strategies. For example, if the network request can be performed in the background you may apply a retry when you got a 5xx HTTP status. Or, if the connection was lost (no response and you get an URLError) you may start to observe the network state and retry when the network is available.

Why does NWPathMonitor status is always satisfied?

When there is no connection I get an error from the URL Session saying that the request timed out.
I’m using the Network protocol to check for connectivity before hand but apparently this is not working as when I am calling this inside viewDidLoad:
static func startUpdateProcess() {
let monitor = NWPathMonitor()
monitor.pathUpdateHandler = { path in
if path.status == .satisfied {
print("Good! We are connected!")
Helper.createDownloadTask()
} else {
print("No connection. Local file not updated!")
}
}
let queue = DispatchQueue(label: "Monitor")
monitor.start(queue: queue)
}
...I get “Good! We are connected!”.
Shouldn’t the path not be satisfied if there is no connection and therefore trigger the else statement?
FYI the createDownloadTask() questions the API and downloads the required data.
Can you tell me what is wrong here and what could I do to get to the else statement if the path is not satisfied?
Thank you!
Credit to user May Rest in Peace for pointing me to the right direction.
Despite the Documentation being silent on the Network Protocol, it seems that the status property of the NWPath class, an enumeration of type NWPath.Status, returns .satisfied as long as the device is connected to a network, regardless of whether that network is working, transmitting data, or not.
The only way the else statement above could be triggered would have been by deactivating Wi-Fi and/or Cellular data or disconnecting from any network before launching the app.
All those properties are listed in the Documentation but none of them has a description or a discussion attached.
This article by user #twostraws allowed me to create the first part of that code.
Reference to the instance of NWPathMonitor (aka monitor in your scenario) needs to be retained.
You could make it a strong property by making monitor a class level property so that its lifecycle is the same as the place you are referring it from. It looks like the monitor object is being released effectively stopping the callbacks for network status monitoring.

Why won't disconnect work?

I'm writing an application which is based on Apple's Temperature Sensor Application for iOS devices. I'm trying to implement a Disconnect button which will disconnect the currently connected device from the iPhone, however when the disconnect button is pressed there is a BAD_ACCESS error, I know this is memory based but I'm completely at a loss on how to fix it. Code follows.
- (IBAction)clickbutton:(id)sender
{
[[LEConnect sharedInstance] startScan:AccelerometerServiceUUID];
}
- (IBAction)disconnectButton:(id)sender
{
CBPeripheral *peripheral;
if(CBPeripheralStateDisconnected)
{
[[LEConnect sharedInstance] disconnectPeripheral:peripheral];
}
}
The startScan button works correctly but the disconnect button does not. The code in the button is based on the code for finding devices shown below:
if (CBPeripheralStateConnected)
{
[[LEConnect sharedInstance] connectPeripheral:peripheral];
[currentlyConnectedDevice setText:[peripheral name]];
}
earlier in this function the same CBPeripheral *peripheral; pointer is made.
Sorry if this is a dumb question or has been asked before, I'm just really struggling and in desperate need of help! Thanks
The disconnectButton method has two errors. First the peripheral variable is used without being initialized (are you ignoring compiler warnings?). Second, the if statement checks if the peripheral is disconnected and then disconnects it again (you should be checking that the peripheral is connected).

iOS Multi peer connectivity showing same device name twice

I am using iOS 7 multi peer technology for connecting my iPad and iPod touch. But whenever iPod touch or iPad goes to sleep it gets disconnected which is fine because multi peer dont work in background mode, but when i discover again it shows iPods name twice in the MCBrowserViewController list. Tried this with every sample code and every code has same issue any one know how to fix this bug.
Also there is one weird issue with MCBrowserViewController if i connect a device and other device accepts it, even though it gets connected MCBrowserViewController will still show as connecting and "Done" button is disabled. I am using MCBrowserViewController and no custom code for this so i guess this is issue from apple.
Also any one knows how to directly connect to the device when app comes back to active state from sleep mode?
Discovering your same name twice is because you do "init" the peerID (withDisplayName) each time you init your session.
From apple's documentation, it's a known bug and you should not do so. Rather, save your peerID somewhere (such as NSUserDefaults), and when you init your session, verify if peerID exists, load it, else create/save it.
The simplest code will look like this:
In the init of your session, replace:
_peerID = [[MCPeerID alloc] initWithDisplayName:XXX];
by:
//If there is no PeerID save, create one and save it
if ([[NSUserDefaults standardUserDefaults] dataForKey:#"PeerID"] == nil)
{
_peerID = [[MCPeerID alloc] initWithDisplayName:XXX];
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:_peerID] forKey:#"PeerID"];
}
//Else, load it
else
{
_peerID = [NSKeyedUnarchiver unarchiveObjectWithData:[[NSUserDefaults standardUserDefaults] dataForKey:#"PeerID"]];
}
Of course, you can make a more sophisticated code, such deallocating it and create it from a dynamic variable in case you wanna change name, etc.
I had the same issue and this is how I solved it,,
In my case I used a UIViewController to handle the connections and every time I open the view I alloc and init the view -viewDidLoad will be called each time- , then in viewDidLoad I initial the MCPeerID & MCSession and thats the problem and this is why we see multi peer connectivity showing twice, so I solved it by doing the initialisation of MCPeerID & MCSession only once in the AppDelegate or a global Class.

ios diagnosing iphone connection status

I am trying to make my iPhone app more robust, by ensuring that it doesn't crash when there is no network connection. Right now the app attempts to make a connection on startup immediately, through the app delegate. It has no issues if wifi or cellular is available, but it will crash if there is no network connection visible. I have looked around on this site and haven't found anything that quite fits my problem. I have the feeling it should be just a simple line of code, like an objective-c equivalent of a pseudo- 'isConnection', or something similar:
if (isConnection) {
- sendSynchronousRequest for json data I'm using
- manipulate the data, etc., and continue with normal operations
} else {
- send an output message to a view controller,
letting the user know what's wrong.
}
I can't seem to isolate the (admittedly abstract) "isConnection" condition that I'm looking for, specifically. Does anyone have experience or advice with this topic?
The reachability class is very easy to use. Download the class files here https://developer.apple.com/library/ios/#samplecode/Reachability/Introduction/Intro.html
You also need to add the SystemConfiguration.framework
Here's the code you need:
-(BOOL)isConnection {
Reachability *reach = [Reachability reachabilityWithHostName:#"www.google.com"];
//replace www.google.com with your own host you're checking for
NetworkStatus hostStatus = [reach currentReachabilityStatus];
if (hostStatus != NotReachable) {
//There are also other status enums like
//ReachableViaWiFi
//ReachableViaWWAN (3G/LTE)
//if you need to detect if user is on cellular, act accordingly
return YES;
}
return NO;
}
Then you can call your method:
if ([self isConnection]) {
//do something
} else {
//no connection, inform user
}
You can use the Reachability class that Apple provides in the Reachability sample application. Not only does it tell you if you're connected, but how you're connected (WiFi, cellular or no connection at all). You can even register for a notification when the connection status changes.
Even though you can use that to check the connection status before initiating the code you describe above, I still think you should investigate why the app crashes when you have no connection, and attempt to resolve it. You could always lose connectivity in the middle of a request attempt, and you don't want that to crash the app either.

Resources