Bool?
For some time now, I have been accumulating a bitterness about APIs returning optional booleans: any API that returns an Bool?
puts the onus, of a default value, on the consumer.
Let’s look at a trivial situation. The client receives an entity that is translated into this struct
.
struct UserProfile: Decodable {
let name: String
let email: String
let isSubscribing: Bool?
}
From a backend perspective, the argument could be: “at this point we don’t know if the user is a subscriber or not”. For example, when the user signed up for the first time.
Although this might be an innocuous remark, when such entity is bubbled up to a profile screen, we, as the consumer, need to decide what to do with with it:
- A
nil
value by default could be consideredfalse
. Although this is what 99% of the time one might do, this doesn’t necessarily equate to the right user experience. The user, might not even know what does it mean to be a subscriber. - A second option, is to replace the
Bool?
with our own type:
enum SubscriptionStatus {
case subscribed
case notSubscribed
case newUser
}
One might argue that this is equivalent to a Bool?
, and from a number of possible cases, it is, but it provides far better context to whoever check the UserProfile
. The case newUser
is named as such, because after the registration, we have no idea about its subscription status.
From a UI perspective, it also makes things clear:
switch status {
case .subscribed:
memberBadge.isHidden = false
case .notSubscribed:
buySubscriptionBadge.isHidden = false
case .newUser:
showNewMemberScreen()
}
versus:
switch status {
case true?:
memberBadge.isHidden = false
case false?:
buySubscriptionBadge.isHidden = false
case nil:
showNewMemberScreen()
}
While most of us are also used to a boolean following the form isSomethingEnabled
, it’s conceivable that an API could return isSomethingDisabled
. This means that the canonical if nil
then false
, no longer holds and one would have to make true
as the default value if nil
.
It’s also quite common for a backend to have at least three consumers: iOS, Android and Web. Which means that each team will decide, what’s the right default three times.
Ok, so does this mean one should never use Bool?
? Throughout the thread, a few people point out cases that have some legitimacy and its usage is warranted. Still, in each case, these could potentially be improved by either:
- Providing an enum to give more context to whoever would use such model object.
- There’s a suitable default value that could be used instead of
nil
.
Nonetheless, it can become unpractical to translate all Bool?
to their own enums. Not only that, the suggestions above assume we control the whole system.