Here’s a doozy of an outlier case where conditional binding could have saved a user experience for an otherwise unhappy consumer. I recently talked with a developer whose production code mixed forced unwraps with a guaranteed API. I wasn’t sure whether to tag this post as Holy War. As you’ll discover, this isn’t your run of the mill development situation.
The deployed-code crashes occurred while querying Apple’s smart battery interface on a Hackintosh. Since the laptop in question wasn’t an actual Apple platform, it used a simulated AppleSmartBatteryManager
interface rather than the Real Thing™. In this case, the simulated manager didn’t publish the full suite of values normally guaranteed by the manager’s API.
The developer’s API-driven contract assumptions meant that forced unwraps broke his app for that user:
Since IOKit just gives you back dictionaries, a missing key, is well… not there, and
nil
. you know how well Swift likesnil
s…
Applications normally can’t plan for, anticipate, or provide workarounds for code running on unofficial platforms. There are too many unforeseen factors that cannot be incorporated into realistic code that ships.
Adopting a universal style of conditional binding enables you to “guide the landing” on unexpected failures, including those failures that occur under less exotic circumstances. Conditional binding lets you introduce a user-facing “bad stuff happened” alert, like the following example:
guard let value = dict[guaranteedKey] else { alertUser("Functionality compromised when unwrapping " + "Apple Smart Battery Dictionary values. Skipping this " + "feature. Please file a bug with full platform info, etc..") return }
Contrast the preceding “safe landing” approach with the more common approaches demonstrated in the following snippet:
// Crash, angry user, bad reviews let value: String! = dict[guaranteedKey] // or let value: String = dict[guaranteedKey]!
Prefer a style that establishes a positive pathway for both recovery and user support. Providing fallbacks and user-facing alerts even when your assumptions are guaranteed to be correct is a always positive coding style.
Universal conditional binding reduces the overhead involved in debugging unexpected Black Swan deployments and allows you to respond with “Sorry pal, my software is only guaranteed to work on official platforms. No refunds.” This practice adds robustness and assumes that in reality bad execution can happen for the oddest of reasons.
Like my posts? Buy a book. Swift Style is available now via Pragmatic Programmer’s Beta Program.
5 Comments
[…] Erica Sadun: […]
It just sounds overly complicated. What would be a clean one-liner, evolves in a separate function call, starting with a guard statement. In principle that’s not too bad, but sometimes it’s deep within library code. Then that particular guard statement would have to return in some way to move the error up to a user-facing part of the code.
I’d use the sketched solution if it wasn’t too much trouble and wouldn’t muck up my clean code. Otherwise, a crash doesn’t sound too bad for the edge case of people running a hackintosh/jailbroken device.
The one liner is not clean. It’s got an ugly forced unwrap on it. The guard version is much better.
I’d show a warning on hackintoshes and be done with supporting idiots
Wether or not you should support hackintoshes seems irrelevant here… if the api uses dictionaries then I would argue that the API is _not_ guaranteeing anything and you should code as such.. unwrap it properly!