Check network connection in background and upload data - ios

I get a requirement to be able to check if there is data with fail status and upload them via webservice.
The flow is gonna be:
User try to upload data but fail because of no internet connection
User stops using app, so the app goes background
Apps start to identify whether internet connection is available
If available, it directly calls web service to upload data
If not available, it will return to #3
Back to my last development on < iOS 7, previously background time was 10 minutes but becomes 3 minutes.
Background Fetch:
When this method is called, your app has up to 30 seconds of
wall-clock time to perform the download operation and call the
specified completion handler block. In practice, your app should call
the completion handler block as soon as possible after downloading the
needed data. If you do not call the completion handler in time, your
app is terminated.
Also that these are the only apps allowed to do background:
Apps that play audible content to the user while in the background, such as a music player app
Apps that record audio content while in the background.
Apps that keep users informed of their location at all times, such as a navigation app
Apps that support Voice over Internet Protocol (VoIP)
Apps that need to download and process new content regularly (Background Fetch)
Apps that receive regular updates from external accessories
Isn't there any way I can do background in iOS yet or at least I can get the network status in background?
Note: My app is for internal distribution only, no need to worry with rejection.

What you need to do is using NSURLSession instead of NSURLConnection (in case you are still using it)
Difference between NSURLSession and NSURLConnection
NSURLConnection: if we have an open connection with NSURLConnection and the system interrupt our App, when our App goes to background mode, everything we have received or sent were lost.
NSURLSession: solve this problem and also give us out of process downloads. It manage the connection process even when we don't have access. You will need to use application:handleEventsForBackgroundURLSession:completionHandler in your AppDelegate
So with the use of NSURLSession, you don't need to manage or to check
your internet connection because OS does it for you.
Extra: How to check internet connection in my iOS app
Disclaimer: I'm not sure that the following Framework can run in background mode
You can use framework Rechability to check if your device have internet connection.
https://github.com/tonymillion/Reachability
It is a drop-in replacement for Apple's Reachability class and it supports blocks to perform action when you receive the NSNotification of reachability changed.
You can check the README.md at his repo but it is very simple to use.
Using NSNotification
1
In your AppDelegate you have to call
[Reachability reachabilityWithHostname:#"www.google.com"];
Also, you will need to declare a property to store that value
#property (strong, nonatomic) Reachability *reach;
so now you will have to put the following code inside your application:didFinishLaunchingWithOptions:
_reach = [Reachability reachabilityWithHostname:#"www.google.com"];
2
In the Controller you need to check if reachability state, you must subscribe to that notification
[[NSNotificationCenter defaultCenter] addObserver:_sharedInstance selector:#selector(reachabilityDidChange:) name:#"kReachabilityChanged" object:nil];
3
Now you can implement the selector reachabilityDidChange: where you check if now are connected or not. Put it in the same Controller.
- (void)reachabilityDidChange:(NSNotification *)note
{
Reachability * reach = (Reachability *)[note object];
if (self.appDelegate.wasConnected != [reach isReachable]){
if ([reach isReachable]){
NSLog(#"Notification Says Reachable");
[[NSNotificationCenter defaultCenter] postNotificationName:#"ConnectionIsReachable" object:nil];
[self sendPendingOperations];
}else{
[[NSNotificationCenter defaultCenter] postNotificationName:#"ConnectionIsUnreachable" object:nil];
NSLog(#"Notification Says Unreachable");
}
self.appDelegate.wasConnected = [reach isReachable];
}
}
As you can see, we store the last connection state also in a AppDelegate property
`#property BOOL wasConnected;`
4
Now you have to subscribe to those new notifications (ConnectionIsReachable, ConnectionIsUnreachable) and perform the action you need to do.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(showNoConnectionAlert) name:#"ConnectionIsUnreachable" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(hideNoConnectionAlert) name:#"ConnectionIsReachable" object:nil];
Using blocks
I have not test it that feature but I copy to you the official documentation
// Allocate a reachability object
Reachability* reach = [Reachability reachabilityWithHostname:#"www.google.com"];
// Set the blocks
reach.reachableBlock = ^(Reachability*reach)
{
// keep in mind this is called on a background thread
// and if you are updating the UI it needs to happen
// on the main thread, like this:
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"REACHABLE!");
});
};
reach.unreachableBlock = ^(Reachability*reach)
{
NSLog(#"UNREACHABLE!");
};
// Start the notifier, which will cause the reachability object to retain itself!
[reach startNotifier];

Related

XCTestCase with internet connection case

I'm writing an app that has separated functions when online and offline. In those function, I use Reachability to check internet connection and with each case (online/offline), it does different works.
Now, I've been required to write test cases for those business logics. I've searched everywhere, but it seems that no one care much about test case in iOS.
I'd like to write testcase that cover for both online/offline case. Does it possible in iOS? If so, how do I simulate the internet connection status?
UPDATE QUESTION:
I also want to cover the case switching from online to offline and vice versa. There should be a way to simulate this network connection status, right?
You should mock return value using OCMock
id reachabilityMock = OCMClassMock([Reachability class]);
[OCMStub([reachabilityMock currentReachabilityStatus]) andReturnValue:#(ReachableViaWiFi)];
[OCMStub([reachabilityMock reachabilityForInternetConnection]) andReturn:reachabilityMock];
Reachability *reachability = [Reachability reachabilityForInternetConnection];
XCTAssertEqual(reachability.currentReachabilityStatus, ReachableViaWiFi);
Then send reachability changed notification manually.
[[NSNotificationCenter defaultCenter] postNotificationName: kReachabilityChangedNotification object: noteObject];

How to check if connection has been switched to 3G while app is in background in iOS?

Is it Possible for an app to know whether the connection has been switched from Wi-Fi to 3G While app is in background?The requirement is that download should happen on Wi-Fi Connection.The Check is done initialy but i need to confirm that download is happening on Wi-Fi and should be cancelled if the user has switched to 3G/4G while app is in Background.
If you just want to make sure downloads only happen on WiFi, then you should set the allowsCellularAccess property of the NSURLSession's NSURLSessionConfiguration to NO.
No monitoring, cancelling or other tricks needed: that'll make sure the download never goes over the cellular connection.
I suggest you this library
Rechability
It uses NSNotification, and you can make some changes in it if you need to set it your way
This is a example code provided by this library
// Allocate a reachability object
Reachability* reach = [Reachability reachabilityWithHostname:#"www.google.com"];
// Tell the reachability that we DON'T want to be reachable on 3G/EDGE/CDMA
reach.reachableOnWWAN = NO;
// Here we set up a NSNotification observer. The Reachability that caused the notification
// is passed in the object parameter
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(reachabilityChanged:)
name:kReachabilityChangedNotification
object:nil];
[reach startNotifier];
Hope it helps

How to correctly handle reconnects to a server in an app

I'm writing on an app which communicates a lot with a server.
When one minimizes the app, didEnterBackground is called I disconnect all connections.
When resuming the app I try to reconnect.
It's all fine if the server is available but I don't know how to handle if an error occurs (server down), as I cannot display anything within the AppDelegate.
Any suggestions how to handle this scenario?
Use NotificationCenter like that:
In your AppDelegate:
- (void)applicationWillEnterForeground:(UIApplication *)application
{
[[NSNotificationCenter defaultCenter]
postNotificationName:#"reconnectToServer"
object:self];
}
In the didLoad-Method:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(reconnectToServer:)
name:#"reconnectToServer"
object:nil];
Add method that is called if your app is active again
- (void) reconnectToServer:(NSNotification *) notification{
// Got to to a ViewController...
ViewController *vc =[self.storyboard instantiateViewControllerWithIdentifier:#"ViewController"];
[self presentViewController:vc animated:YES completion:nil];
}
If you're looking to notify the user, you could consider using the NSNotificationCenter to publish failure messages and then you could respond to these from other locations?
Sure you can. UIAlertView can be created and shown from anywhere. The app delegate also has access to the app window(and thus the root view controller) so it could inject views.
Note that I'm not saying it's correct to have this functionality in the app delegate, because it isn't. You should have a network controller / monitor which handles this.
Many sorts of things may help you for check server validation.
If server down, you not receive any data in few seconds. This is may be error for user.
If server may down, you can use some sort of "ping" first. If ok, acquire a data, if not show server unreachable.
If you use iOS API like NSURLConnection, NSURLRequest, NSURLResponse you can setup timeout for requests, perform sync request, and block/delegation requested data validation.

How to get Reachability Notifications in iOS in Background when dropping Wi-Fi network?

I'm using Reachability successfully to determine the status of the network, and to be notified of changes (e.g. Reachability Guide for iOS 4).
My question isn't how to get Reachability up and running, but rather the following.
My AppDelegate handles the Reachability stuff. The app receives notifications (kReachabilityChangedNotification) while the app is running, and when the app is in the Background (applicationDidEnterBackground:).
The app is designed to reload a playing audio stream when it notices that it's lost a Wi-Fi connection, e.g. To test, I turned Wi-Fi on and off in Settings, and everything worked perfectly. In real-world testing, I often lose Wi-Fi connectivity when I exit the range of the access point. I've found that Reachability isn't helping me too much in this case. I'm not sure if it's because Reachability notifications don't come through when the screen is locked, or if Reachability doesn't handle the slow diminishing of signal from an increasingly distant Wi-Fi access point, but regardless I can't figure out why the real-world testing doesn't match the idealized case.
This is what my code looks like. I first set up to receive notifications, and start listening to Reachability:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// check for internet connection
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(checkNetworkStatus:)
name:kReachabilityChangedNotification object:nil];
// Set up Reachability
internetReachable = [[Reachability reachabilityForInternetConnection] retain];
[internetReachable startNotifier];
....
return YES;
}
and then, this is the function that responds to changes in connectivity:
- (void)checkNetworkStatus:(NSNotification *)notice {
// called after network status changes
NetworkStatus internetStatus = [internetReachable currentReachabilityStatus];
switch (internetStatus)
{
case NotReachable:
{
NSLog(#"The internet is down.");
break;
}
case ReachableViaWiFi:
{
NSLog(#"The internet is working via WIFI");
break;
}
case ReachableViaWWAN:
{
NSLog(#"The internet is working via WWAN!");
break;
}
}
}
The notifications come through even when the app is in the background, but they don't in the real-world testing described above.
Thanks for any help.
By default in the background state app stays for a short time only, most apps move to the suspended state shortly afterward. That mean the app is in the background but is not executing code. So your custom implemented notification do not work.
Must requery NetworkReachability at Wakeup Time in app delegate methodes:
applicationWillEnterForeground:
applicationDidBecomeActive
I was working on a VoIP app, which is launched in the background when the iPhone boots up, at which point there might be no network reachability (e.g. if the phone has both a passcode and/or a SIM card with a PIN code). But since the app is launched directly in the background, the delegate method applicationDidEnterBackground: is not called. Instead what I did was use #Hurden's idea directly in application:didFinishLaunchingWithOptions, checking the applicationState to see if the app was actually starting in the background. This enabled my app to get the kReachabilityChangedNotification notification after the phone was unlocked (enabling the WiFi to connect using the stored password).

Proper way to check for Network Connection in iOS

I'm using the Reachability class to monitor network connection and server availability in a couple of iPhone apps. I have the following code in my AppDelegate:
// Observe the kNetworkReachabilityChangedNotification. When that notification is posted, the
// method "reachabilityChanged" will be called.
[[NSNotificationCenter defaultCenter] addObserver: self selector: #selector(reachabilityChanged:) name: kReachabilityChangedNotification object: nil];
// Start checking for reachability to myWebService.com
//Change the host name here to change the server your monitoring
hostReach = [Reachability reachabilityWithHostName: #"myWebService.com"];
[hostReach startNotifier];
internetReach = [Reachability reachabilityForInternetConnection];
[internetReach startNotifier];
wifiReach = [Reachability reachabilityForLocalWiFi];
[wifiReach startNotifier];
When I profile one of the apps (a dedicated blog reader for my companies blog) using Instruments, I see a difference of +5.9 Mb of network traffic over a one minute period when I turn on the notifiers, vs commenting those lines out.
What is the proper way to check for network connection before sending a request to a server? I probably shouldn't be monitoring the connection full time, but only when I need to know.
My other app communicates with a web service API to post pages and video, and I want to know if I have a connection to that service before attempting to post, but I want to minimize network traffic where I can.
I'd guess that this is one thing Apple looks for when approving/rejecting apps in the app store.
Instead of checking the connection before you make the request, just check for a network connection error when you do make the request. You can periodically make requests if you want to update the status of the network.

Resources