Why don't downloads switch back to wifi after been on a cellular network? - ios

I have encountered a rather strange behavior when trying to implement download functionality on iOS. The download implementation works fine in that it finishes successfully, can run in the background, and file is stored on device. However during a download, I can turn of wifi to let the task switch to and continue on cellular network (or just start the download using cellular). This behaves as aspected. But when I enable wifi again, the download never seem to switch back to using wifi. The device is connected, and the wifi-connection-bars displays at the statusbar. Using rechability functions to check what connection that is available will even return Wifi, but the download seems to be stuck at using cellular.
The way I am detecting this is looking at the Usage stats in the system settings. The cellular data usage will rise in sync with the pending download, and continue to rise until the download is finished (even if wifi is turned on again).
I have tested with both Alamofire and by using NSURLSession and NSURLSessionDownloadTask directly, and they both behaves similarly. I have also seen this behavior in two completely seperate projects, on multiple devices, in iOS 8.4 and 9.1, when the apps are in the foreground or the background, and even AppStore behaves like this when downloading apps!
Has somebody else experienced this?
And if so, did you find any way to gracefully switch tasks back to wifi?
Thanks in advance.

This is normal behavior. Adding a new network interface (e.g. turning Wi-Fi on) doesn't stop existing TCP connections. They will continue until the original network interface goes away.
If you want to pause the request and reconnect when Wi-Fi becomes available, you'll need to call cancelByProducingResumeData: on the task, then create a new request with that resume data to restart the request from where it left off. That new request will go over the currently active network interface, which would typically be the Wi-Fi interface if Wi-Fi is up and running.
Before you stop the existing request, though, I would suggest trying a probe request for something like Google's generate 204 or one of Apple's captive portal detection URLs, to ensure that Wi-Fi really is working.

Related

How to get connected/disconnected information for the apps with Xamarin.Android and WearOs?

I am trying to implement communication between WearOs watch and Android phone Apps. Here is the key point is between Apps, not between Devices because for me important is that, i can get the information about if the watch app is connected with the Phone app and vice versa. But it seems that On Android and WearOs, it happens on device level.
I have implemented WearableListenerService on both side and works perfectly fine. OnCapabilityChanged is fired as well but problem with the OnCapabilityChanged method is only fired if connection is lost and connected again. It is not fired when phone and watch are already connected and when I start the app on the phone, I am not able to know if there is already connection.
OnPeerConnected seems to be a good candidate in this sense but it is deprecated and never fired.
What is the best way to get this information? I have tried also Activity level CapabilityClient.IOnCapabilityChangedListener on MainActivity but OnCapabilityChanged behavior seems to be exactly the same. It is not called when the activity is started.
I have tried to implement code below also in OnResume but it returns always null.
var capabilityInfo = capabilityClient
.GetCapability(capability_wear, CapabilityClient.FilterReachable);
capabilityInfo.AddOnSuccessListener(this);
public async void OnSuccess(Java.Lang.Object capabilityInfo)
{
}
Beside this question, related question, is it even possible to disconnect phone app to watch apps?
I have had similar experience on Tizen watches and There is a SocketService with Connect and Close endpoints implements. Simply can be connected between Apps. But here on WearOs, it seems to happen on Device level only.
What's your definition of "watch app is connected"?
The Node (device) is connected and the app is installed? Or do you mean the app is running.
If you just mean app installed and device connected, then your code should work. Are you using a custom capability that your app declares? That should prove it is your app installed and not just the device connected.
File a bug using a reproduction in the https://github.com/android/wear-os-samples/tree/main/DataLayer project. https://issuetracker.google.com/issues/new?component=1065087&template=1592236
https://developer.android.com/training/wearables/data/data-layer
The CapabilityClient provides information on which Nodes on the Wear OS network support which custom app capabilities.
If you mean connected as in both running simultaneously then you would probably use ChannelClient, MessageClient, or DataClient to send observable updates. But this should generally be careful how you do that as you shouldn't try to maintain a live connection, say using the ChannelClient, between your watch and mobile apps. It will waste battery on both sides and there are hopefully more efficient patterns to achieve whatever you are trying to do.

APIs are not responding fast is APP is using Multipeer Connectivity framework

I am using Multipeer Connectivity Framework in my APP and its working absolutely fine but when APP is invoking any API or downloading any file from server then its delaying the response. Sometimes its failed and sometimes I am getting Time out error.
I have also checked "Network" status on xCode debug navigator and found that speed is going up and down instantly and never going beyond 6kb/s and sometimes its showing as 0 kb/s but when I disable Multipeer Connectivity then it works fine and speed going at 70kb/s too.
I have also noticed that if bluetooth is ON then its happening not with the Wifi.
Any suggestion would be really appreciable.
This is happening because when advertising, Multipeer always advertises on the wifi (there's no way to say bluetooth only). The way it advertises is by switching the wifi access mode into adhoc multiple times per second to find other peers, then switching back to AP mode (ie. resuming its connection to your wifi router). Doing this is highly disruptive to large transfers.
You have two options to fix this:
1) as soon as you can, after getting connected, call stopAdvertising(). This will stop the wifi mode from being constantly changed, and your throughput will resume at its normal rate. Warning: you cannot micro-manage this, because it takes up to 30 seconds after calling stopAdvertising() until it takes effect
2) switch from Multipeer to an alternative framework that allows you to specify bluetooth-only, and only use bluetooth. I wrote one called BluePeer which I use in my apps. It is unicast (not multicast) and supports roles like Client/Server (as well as role-less like Multipeer)

Failure to reconnect after De-authentication from Cisco AP Wifi

Apologizing in advance, I am no 802.11 expert and this is a long winded question...
I am working on an iOS voip client, we use the Cocoaasyncsocket library for our TCP/UDP connections. The app/iDevice is allowed to roam in/out of wifi coverage (for the purposes of this discussion we will assume the app is using wifi only... no cellular connection). We have the appropriate plist settings for backgroundmode (voip, audio) as well as requiring persistent wifi.
We are having a problem that seems particular to Cisco AP's. With the client app in the foreground and roaming out and in of wifi range, we noticed that eventually the iOS device will eventually not automatically rejoin the network. After a great deal of debug the failure to rejoin was noted to have nothing to do with the app. The failure to re-join can ultimately be reproduced by forcing the AP (via config) to deauthenticate the iDevice three times. After the 1st and 2nd deauthentications, the iDevice readily automatically re-joins. But after the 3rd time, iOS does not automatically rejoin the network.
The network will be rejoined if, for example, the iOS email app is put in the foreground.
We were curious if any other VOIP type apps suffered this problem, and ran an experiment with running FaceTime and Skype on the iOS device.
Skype behaved much like our voip app, after the 3rd deauth the connection was lost. Trying to initiate another call resulted in a message to the effect of "must have an internet connection to make a call".
FaceTime did disconnect on the 3rd deauth and failed to automatically rejoin... however, we were able to re-initiate another FaceTime call which caused iOS to rejoin the network and make the call.
We would like to emulate the FaceTime behavior, but so far do not understand what we should be doing differently. To the best of our knowledge, we are properly closing the open sockets when we get disconnected. Is anyone familiar with this issue and have some insight to offer?

iOS Reachability when app is in Background

My application requires exact information on whether or not user is connected to Wifi network and not on internet connectivity so do not comment on the use case please. The task is: user needs to verify that their Wifi is connected but if they dont have any Wifi network available to connect to, they can do it later. For this I need my app to notify the user when he later connects to a Wifi network that now you can continue with the test.
Can I accomplish this using Reachability in background fetch request? Are there any limitations to tasks that app can perform in Background fetching?
This is not solvable in the general case. You cannot run arbitrary code in the background for an indefinite period of time (unless you already are an allowed background task for other reasons, such as location services or VoIP, but these still have restrictions). And you cannot request to be launched when reachability changes.
You can, however, configure an NSURLRequest to not use cellular access. That's not exactly the same as "wireless" but it's close and may be what you actually mean (you're not clear on why "wireless" is the criteria). You can then use NSURLSessionUploadTask or NSURLSessionDownloadTask to ask the OS to perform the action when possible (without waking you up or involving you in any way when you're in the background). If that request were to your server, you could then use a push notification to alert the user and achieve the experience you're describing.

NSURLSession background download over cellular possible in iOS 8?

I need to make small downloads initiated when my app is in the background and woken up by Significant Location Changes. BUT Apples documentation of NSURLSessionConfiguration here:
https://developer.apple.com/library/IOs/documentation/Foundation/Reference/NSURLSessionConfiguration_class/index.html#//apple_ref/occ/instp/NSURLSessionConfiguration/discretionary
says the following for the discretionary property:
When transferring large amounts of data, you are encouraged to set the
value of this property to YES. Doing so lets the system schedule those
transfers at times that are more optimal for the device. For example,
the system might delay transferring large files until the device is
plugged in and connected to the network via Wi-Fi. The default value
of this property is NO.
The session object applies the value of this property only to
transfers that your app starts while it is in the foreground. For
transfers started while your app is in the background, the system
always starts transfers at its discretion—in other words, the system
assumes this property is YES and ignores any value you specified.
This seems to imply that if a download is started in the background the OS always has discretion as to whether and when to proceed with the download. It seems that the OS is always waiting for a wifi connection before completing these tasks. My experience supports this conjecture. I find that I can send several notifications for downloads while device is on cellular. They remain stuck. When I switch the device to wifi they all go through.
WTF? Why Apple prohibits configuring this behaviour, especially since they HAVE the neccessary properties on the NSURLSessionConfiguration object (discretionary, allowsCellularAccess). Of course the user is not always in WiFi when download tasks are started. I need to do this in response of app wake-ups by significant location changes. I schedule download tasks on a background session and never receive delegate callbacks on cellular network until I switch to WiFi. So does that mean I cannot finish these tasks unil WiFi is available again or I bring the app back to foreground?
Has anybody of you a workaround for this?
I already thought of the following:
Starting a long running background task (old style) after a location update and download my stuff there by using a defaultSessionConfiguration. Any experiences if that can work? If this doesn't I will use NSURLConnection I think (even more old style).

Resources