Swift Snippet : Map Bool values with Generics in Swift

My problem

It often occurs while writing code that you need to assign a value of a certain type, based on the value of Boolean. To do this there are multiple ways, but the 2 most known are

1. The let’s-write-a-lot-of-code approach

let isAWildBoolean = true

// Set the value of `offSet` to a value based on the Boolean
let offSet:Int

if isAWildBoolean {
    offSet = 10
} else {
    offSet = 20
}
Swift

I don’t like the first approach, because it creates a bloated codebase. A better way is to use the ternary operator (also known as the inline-if).

2. The one-line-star-developer (ternary operator) approach

You specify the Boolean you want to evaluate, add a question mark, followed by the value if the Boolean is true, followed by a colon, followed by the value if the Boolean is false.

var isAWildBoolean = true
let offSet = isAWildBoolean ? 10 : 20
Swift

This is already a big improvement. We went from 6 lines of code to 1 line …
but I have 2 issues with the ternary operator:

  • Readability: People who are not familiar with this operator might be overwhelmed by the question mark – value – colon – value and just go bananas in their heads.
  • Performance: The ternary operator is a real snail when it comes to compile performance. There is an outstanding issue on the Swift Bugs website about this.  To give you an example of how slow it is I created 2 methods.  One method uses the ternary operator, the other one just a regular if/else statement.
func returnResultWithTernaryOperator( value:Bool ) -> String {
    
    return value ? "it's true" : "it's false"
    
}

func returnResultWithIfStatements( value:Bool ) -> String {
    
    if value {
        return "it's true"
    }else {
        return "it's false"
    }
    
}
Swift

I’ve hooked up the Build Time Analyzer tool to check how long it takes for both methods to compile.

Performance between ternary operator and if-else statement

The method with the ternary operator takes 0.4ms to compile, and the method with the if-else statement takes 0.1ms to compile. That’s four times slower! On small codebases you won’t feel it, but our codebase for example takes 1 minute and 40 seconds to compile. So I’m in the process of removing all ternary operators (and other things that increase compile times) in our codebase.

3. My current solution

I’ve created an extension on the Boolean type that uses generics to return a value of a specific type and is also (in my opinion) more readable than the ternary operator. It looks like this.

import Foundation

extension Bool {
    
    /// Convenience method to map the value of a Boolean to a specific Type.
    ///
    /// - Parameters:
    ///   - ifTrue: The result when the Boolean is true
    ///   - ifFalse: The result when the Boolean is false
    /// - Returns: Returns the result of a specific Type
    func mapTo<T>( ifTrue:T, ifFalse:T ) -> T {
        
        if self {
            return ifTrue
        } else {
            return ifFalse
        }
        
    }
    
}
Swift

https://gist.github.com/frederik-jacques/8efb3f0a8cbd46aa760880878a650931

At first I tried it with the ternary operator, but the ternary operatorgenerics, made it compile 6 times slower! So the obvious choice was to just use an if/else statement. To demonstrate that it really is this slow I’ve created 2 methods that do exactly the same thing. The only difference is that one uses a ternary operator, and the other one a regular if/else statement.

extension Bool {

    func mapToTernary<T>( ifTrue:T, ifFalse:T ) -> T {
        return self ? ifTrue : ifFalse
    }
    
    func mapTo<T>( ifTrue:T, ifFalse:T ) -> T {
        if self {
            return ifTrue
        } else {
            return ifFalse
        }
    }

}
Swift
Performance between ternary operator and if-else statement with generics

It’s sad you have to discard cool language features, just because they compile slower, and I’m sure these things will get fixed eventually in a next Swift update.

But still the overall result is a more readable way to express values of a different type based on a Boolean.

enum ViewState {
    case loading
    case empty
    case list
    case error
}

let isEmpty = false
let viewState:ViewState = isEmpty.mapTo( ifTrue: .empty, ifFalse: .list )
Swift

If you have other ways of dealing with these kind of things or have other suggestions, just leave a comment and let’s have a discussion!

Share this post

One Response

  1. Would you please update your Unity – iOS tutorial for Unity 2017? Seems like it crashes with EXC_BAD_ACCESS. Thanks!

Leave a Reply

Your email address will not be published. Required fields are marked *

Related Posts