February 13, 2019 – Thomas Senkman – 8-minute read
For an unknown reason, a significant number of users disable the mobile data for our iOS app. This is not a problem when they are booking a car from their couch at home with Wi-Fi, but can quickly become a major issue when they try to unlock their Drivy Open car in the street. At this specific moment, there is very little chance that they remember they disabled this setting, so it very often leads to a call to Customer Services that could have been avoided.
If we do nothing about this, the users’s resquests would always fail when not on Wi-Fi, and they would see a default error. In our case, they would see the message “An error has occurred”, which doesn’t help them to understand what’s wrong. However, it’s our job to let the users know what they can do to fix the issue.
To try to solve this, according to our tests, iOS shows an alert when all these conditions are met:
Its UX is nice, because it redirect the user to the correct screen to update the setting with a single tap. But this doesn’t cover all the cases: if a first Wi-Fi call is successful, even with the mobile data setting disabled, the user will never see the alert.
Because we think it’s not sufficient, we decided to reimplement this alert for each network call that fails because of this specific setting.
The only requirement is to have an iOS 9 app target, as we will use
CTCellularData which is only available from this version. To our knownledge there is unfortunately no way to check the network-data setting value before.
So since iOS 9, Apple provides in
CTCellularData a listener to check the value of the mobile data switch for the current app.
We’ll also need to be able to check for reachability to know if we are in our specific error case. As we use Alamofire in our app, we used their
NetworkReachabilityManager, but you can also use another solution like Reachability.swift or even add Apple’s
SCNetworkReachability class to your app for this.
Implementing the listener to get the current value is pretty straightforward:
Same logic here, we just have to explicitly start the listener after setting it:
Checking reachability can be done at any moment, but is not instant. So if you init the
NetworkReachabilityManager and directly try to get the current status, this will probably fail. To avoid this, and because this does not consume a great deal of memory, we can have our own manager that stores the current value whenever it changes:
We strongly advise to check both statuses, especially reachability, only when network errors happen. You should never check for reachability before making a network call to potentially avoid making it. If there is an issue with the reachability, this would result in blocking all the network calls of your app. This is even the first “important thing” Alamofire says in their documentation:
Do NOT use Reachability to determine if a network request should be sent. You should ALWAYS send it.
Here is our singleton manager, which contains:
We need to start the listeners at some point, we’ve chosen to do it directly at app launch, in AppDelegate since our app needs network calls directly:
Then, in your view controller, you can simply call the
checkApiReachability method in case of error:
It’s kind of strange to reimplement a native alert, but we were really surprised by iOS’s
incomplete basic version, which isn’t that much of a help in our case. We only did this recently so we don’t have enough data to draw conclusions, but we hope this will avoid some calls to Customer Services.
And don’t forget to never rely on reachability before making the actual network call: it should always be an error handling helper.