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:

  1. nil value by default could be considered false. 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.
  2. 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:

  1. Providing an enum to give more context to whoever would use such model object.
  2. 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.