It’s very important–thought easy to overlook–that you test your app under varying levels of network connectivity, or at least consider what they might mean for your design. Those levels are:
- Full connectivity to the Internet, with unmetered data and high speed transfers. This is the situation that we typically assume, because it means unrestricted access to network resources and whatever data we might want to acquire or share.
- Limited connectivity (local network only). This is where there could be some access to resources on other local devices, but Internet resources are not available. This would only be a concern for apps that are specifically written to communicate with themselves or related apps on a local network (via IP addresses and mechanisms like sockets) and are not relying on remote resources from http[s] sources and such.
- Connectivity with high latency. This means having connectivity to the resources you need, but the data comes down slowly, perhaps much more slowly that you expect. This is a concern where overall app performance is concerned. For example, an app might assume that it can just wait until the data is available to refresh its display and not show anything like progress indicators and such to tell the user that the app is waiting for some operation to complete. I’ve seen apps that assume they get their data in short order, but when there are timeouts or other such problems, the app becomes unresponsive or uninformative. Apps that do a better job have built-in resiliancy to timeouts, builthave -in retry mechanisms, and keep the user updated as to the status of network connectivity. (As an example, some of the system UI will say things like “Connecting to a network is taking longer than usual” with an option to cancel.
- No connectivity (local or internet). This is the most important one to consider, becaue many apps, assuming the best where connectivity is concerned, are not written to gracefully handle the lack or loss of connectivity, and oftentimes just end up in a state where they appear hung. Worse still, some apps just fail to deliver any meaningful user experience without connectivity and give no suggestion that connectivity might be the problem. A bad app in this regard fails to give the user a means to retry whatever they might have done earlier, if they’ve taken steps to remedy connectivity problems. In these cases, the user is forced to close the app and launch is again, hoping for the best.
- Metered connectivity (typically a mobile broadband network that might be pay-as-you-go or possibly nearing its data limit). The Store certification requirements state that apps that do large transfers need to be sensitive to possible overage charges. See section 4.5 of those requirements for details. Fortunately, the Background Transfer APIs in WinRT pay attention to this, but if you’re using your own mechanisms you’ll need to be attentive to metered situations.
- Roaming a device between networks. For the most part, apps don’t need to concern themselves with this if they’re handling the other situations above. Windows itself automatically handles roaming to a new network and choosing the best one of those available. Sometimes this means entering a metered network, sometimes it means leaving one. Sometimes the network speed will increase, other times it will decrease.
You might also consider airplane mode, meaning that wi-fi is disabled by a wired network could still be used, though this is generally a case of “no connectivity” above when a wired connection doesn’t exist.
Whatever the case may be, the important event to know about and listen to is the Windows.Networking.NetworkInformation.onnetworkstatuschanged event. This is fired whenever there’s a change in connectivity, including cost. Similar to this event is aso the networkStatusChange background task trigger, which you can use to execute a small piece of background task code for this condition if the app itself is suspended or not running.
Now knowing what conditions to test for is one thing; the second part is knowing when to test for these, that is, what app states to test along with the different levels of connectivity. The primary ones here are:
- During first startup: what happens if the app is launched (from its tile and through whatever contracts it supports, such as Search and being a Share target)?
- During subsequent startup: same tests as above, but now with the possibility that previously-cached data is available for offline use.
- While the app is running; what happens when connectivity levels change while the app is active? This is important to test both on single pages that might make network requests, and especially on page transitions where the app might be making network requests to populate the new page with data.
The most reliable and robust apps will respond to different conditions at these different times in such a way that the user is never left confused as to what’s going on and what they might be able to do about it. The worst thing for an app is where the user ends up staring at a blank screen without any idea of what might be wrong. I’ve seen this with some apps, where I tried to launch them in airplane mode after just installing them while waiting for a flight, or when I run the app after having travelled somewhere and not having connected to the Internet yet.
It’s also very confusing when the app is working just fine, but goes through an indeterminate visual state while waiting for its data. Let’s say that you have the kind of app that shows a list of search results, then navigates into a details page for one of those items. Let’s say that under normal conditions that list of results comes down within half a second and acquiring the details within a similar timeframe. With those timings, the app doesn’t need to pay attention to visual details going between the results and details pages. However, increase those times to two seconds or more and a user could see blank screens or otherwise unresponise UI for too long a time. This could easily happen when the user is in a hotel or other situation with a shared internet connection that is handling many demands from multiple users. It could also happen where the servers providing all that data are slow to respond for whatever reason (say, a problem at one data center that is placing a larger-than-usual demand on the others).
The answer to such times may be as simple as putting up a progress indicator or other message that tells the user to wait a bit (something that’s good to do after two seconds of waiting). But these things often need to be designed, and should involve whatever designers you worked with on the rest of the app’s UX because such things are, in the end, part of that user experience. This is important to understand, because you often don’t see the need for such designs until you’re well into development and testing of the app. So be sure that you can go back to your designers for some of these concerns when you need to.
In some cases you can eliminate some of the delays with clever implementation. In apps with results-details pages, it’s not necessary to completely navigate away from the results page to show the details page, that is, it’s not necessary to tear down the results page when showing the details page. Instead, just create and show the details page on top of the results page. In HTML/CSS, this just means placing a full-screen div for the details page over the top of the results page. That way, when navigating back to the results page, which is often a very common activity, you just hide or tear down the details page, letting the pre-existing results page just become visible again. (You’ll want to determine how long it’s been since it was last visible, in case it’s appropriate to refresh the data.) This way you avoid most needs to reacquire the data for that page, thus eliminating a case where connectivity is a concern.
Whenever you acquire data, too, consider caching it in some way so that you can start the app again without connectivity and have something meaningful to show. There have been times where I know I’ve been using an app, as I have a website, and wanted later to return to that same data/page to see something again. So if connectivity has been lost, and the app is reloading that previously saved data, then I get to see something familiar instead of a blank page and a message saying there’s nothing to show. With cached data, at least I can see and interact with what I saw before, which may, in fact, be exactly what I wanted.