Avoiding Preflight Checks for Cellular access

After watching the WWDC session on networking and hearing general advice to avoid using Reachability for pre-flight checks, I was reminded of a time I do implement a pre-flight check. I looked at removing this from my app, but I ran into a snag. I was hoping to get some advice on the following scenario:


I have an app that lets users download videos to their device. If they are on cellular, I present a warning that they will be downloading XX MB on cellular, and if they accept, I toggle an option in UserDefaults to allowCellularDownloads.


The relevant code looks like this:


// if we're on cellular and cellular downloads are turned off, then the download
// will start in a pending state and continue when wifi is reached. We don't get
// a notification or callback for this, so the UI will look like the UI is stuck
// instead we'll check now and update the messaging to better communicate this
let onCellular = self.reachability?.status == .reachableViaWWAN

Then we configure an alert view with an action that reads the user defaults setting:


alert.addAction(UIAlertAction(title: "Download Over Cellular", style: .default, handler: { action in
  
    if Settings.shared.allowDownloadingOverCellular {
        self.startDownload()
    } else {
        let confirm = UIAlertController(title: "Allow Cellular Downloads?", 
          message: "This can be adjusted later in settings", preferredStyle: .alert)
        confirm.addAction(UIAlertAction(title: "Enable", style.default, handler: { ... })
        confirm.addAction(UIAlertAction(title: "Download When on Wi-Fi", style.default, handler: { _ in
            // will just pause silently until we are on wifi
            self.startDownload()
        })
        // ...
    }



The downloader sets the allowsCellularAccess on the session used for downloads based on their setting:


let config = URLSessionConfiguration.background(withIdentifier: sessionIdentifier)
config.allowsCellularAccess = Settings.shared.allowDownloadingOverCellular


In this scenario, how do I properly communicate this to the user without using reachability?

I’m not sure if your current code does what you think it does. You’re running these downloads in a background session, and background sessions will generally not use WWAN for downloads [1]. So, for most users your current behaviour will be something like this:

  1. User starts a download while on WWAN.

  2. You detect they’re on WWAN and put up an alert.

  3. User agrees to WWAN use.

  4. You configure the background session to allow WWAN.

  5. You start the download.

  6. The background session defers the download until the user is on Wi-Fi.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

[1] Background sessions can use WWAN, but they strongly prefer Wi-Fi. I’m not sure of the exact circumstances where a background session uses WWAN, but for most users the behaviour will be to defer a download rather than download over WWAN.

Ah, that is certainly not what I was expecting.


For the sake of completeness, let's assume I was not using background sessions for this task... how would I avoid the preflight check here? The actual download is quite removed from the view controller. It looks something like this:


VC -> DownloadController -> DownloadOperation

For the sake of completeness, let's assume I was not using background sessions for this task... how would I avoid the preflight check here?

Something like this:

  1. Start with the session configured with

    allowsCellularAccess
    set to false and
    waitsForConnectivity
    set to true.
  2. Issue the request.

  3. If the request goes through, you’re all done.

  4. If not, you’ll get the

    -URLSession:taskIsWaitingForConnectivity:
    callback.
  5. Now that the know the request has stalled (look Ma, no preflight!), you can examine the current networking environment for remedial options. For example, if it looks like WWAN is available, you can put up your dialog and ask the user whether they want to enable that.

You can implement step 5 in one of two ways:

  • You could use reachability or

    nw_path_monitor_t
    .
  • You could issue a

    HEAD
    request to the server to see if that goes through.

There’s the usual pros and cons to each.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"
Avoiding Preflight Checks for Cellular access
 
 
Q