43

I was converting from Swift 2 to Swift 3. I noticed that I cannot convert a boolean value to integer value in Swift 3.

let p1 = ("a" == "a") //true

print(true)           //"true\n"
print(p1)             //"true\n"

Int(true)             //1

Int(p1)               //error

For example these syntaxes worked fine in Swift 2. But in Swift 3, print(p1) yields an error.

The error is error: cannot invoke initializer for type 'Int' with an argument list of type '((Bool))'

I understand why the errors are happening. Can anyone explain what is the reason for this safety and how to convert from Bool to Int in Swift 3?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Shubhashis
  • 10,411
  • 11
  • 33
  • 48

9 Answers9

71

You could use the ternary operator to convert a Bool to Int:

let result = condition ? 1 : 0

result will be 1 if condition is true, 0 is condition is false.

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
  • 3
    Ok, so casting from Boolean to Integer value is a no go in swift 3? – Shubhashis Oct 25 '16 at 14:42
  • 3
    I don't believe this conversion was really ever part of Swift. I believe it was a side-effect of implicit conversions through NSNumber. Implicit Bool/Int conversions are an old source of bugs in C (in particular, because non-zero numbers like "2" is "true-ish" but not equal to `true`). Swift has actively tried to avoid these historic sources of bugs. – Rob Napier Oct 25 '16 at 14:57
65

Swift 5

Bool -> Int

extension Bool {
    var intValue: Int {
        return self ? 1 : 0
    }
}

Int -> Bool

extension Int {
    var boolValue: Bool {
        return self != 0 
    }
}
Community
  • 1
  • 1
Nike Kov
  • 12,630
  • 8
  • 75
  • 122
21

Try this,

let p1 = ("a" == "a") //true
print(true)           //"true\n"
print(p1)             //"true\n"

Int(true)             //1

Int(NSNumber(value:p1)) //1
Nagendra Rao
  • 7,016
  • 5
  • 54
  • 92
13

EDIT - From conversations in the comments, it is becoming clearer that the second way of doing this below (Int.init overload) is more in the style of where Swift is headed.

Alternatively, if this were something you were doing a lot of in your app, you could create a protocol and extend each type you need to convert to Int with it.

extension Bool: IntValue {
    func intValue() -> Int {
        if self {
            return 1
        }
        return 0
    }
}

protocol IntValue {
    func intValue() -> Int
}

print("\(true.intValue())") //prints "1"

EDIT- To cover an example of the case mentioned by Rob Napier in the comments below, one could do something like this:

extension Int {
    init(_ bool:Bool) {
        self = bool ? 1 : 0
    }
}

let myBool = true
print("Integer value of \(myBool) is \(Int(myBool)).")
diatrevolo
  • 2,782
  • 26
  • 45
  • Or combine @eric-aya 's ternary op recommendation with a protocol for maximum conciseness! – diatrevolo Oct 25 '16 at 14:57
  • 1
    This approach has generally been discouraged by the core Swift team. They recommend `Int.init` overloads rather than methods to perform full-width conversions. – Rob Napier Oct 25 '16 at 14:58
  • Any documentation I could read to that effect @Rob-napier? I'd like to know more about why it's discouraged and the discussion leading to that decision. – diatrevolo Oct 25 '16 at 15:01
  • I can't put my hands on a conversation right now; buried somewhere in swift-evo. But you can see the results of the opinion by watching how stdlib has evolved. For example, see https://github.com/apple/swift-evolution/blob/master/proposals/0006-apply-api-guidelines-to-the-standard-library.md and note where `...value` methods have been converted to `init` (there were never many `...value` methods in stdlib, but see `ObjectIdentifier` and `StaticString`). Note also in the Swift 3 style guidelines that type conversions are only discussed in terms of init's. Never methods. – Rob Napier Oct 25 '16 at 15:26
  • Cool, more curious than anything. Thanks! – diatrevolo Oct 25 '16 at 15:27
  • I wish I could put my hands on a specific "here is the history," but it's much more "every time I bring up `.array` on `LazySequence` to replace `Array.init`, I'm reminded by one of the team that there is a preference for `init`. – Rob Napier Oct 25 '16 at 15:27
5

Swift 5.4

This is a more generic approach which is applicable for other types than just Int.

extension ExpressibleByIntegerLiteral {
    init(_ booleanLiteral: BooleanLiteralType) {
        self = booleanLiteral ? 1 : 0
    }
}

let bool1 = true
let bool2 = false

let myInt = Int(bool1) // 1
let myFloat = Float(bool1) // 1
let myDouble = Double(bool2) // 0
let myCGFloat = CGFloat(bool2) // 0
1

You could use hashValue property:

let active = true
active.hashValue // returns 1
active = false
active.hashValue // returns 0
1

unsafeBitCast is always an option

let int: Int = Int(unsafeBitCast(someBool, to: UInt8.self))
  • 1
    Offering an explanation is _always_ useful on Stack Overflow, but it's _especially_ important where the question has been resolved to the satisfaction of the community. Help readers out by explaining what your answer does different and when it might be preferred. Can you [edit] your question to add more detail? – Jeremy Caney Jan 01 '22 at 00:30
  • That doesn't seem safe – clearlight Dec 12 '22 at 18:45
0

Tested in swift 3.2 and swift 4

There is not need to convert it into Int

Try this -

let p1 = ("a" == "a") //true

print(true)           //"true\n"
print(p1)             //"true\n"

Int(true)             //1

print(NSNumber(value: p1))   
user1101733
  • 258
  • 2
  • 14
0

In swift 5:

you can do this:

let x = ("a" == "a")

Int(truncating: x as NSNumber)
Xiaomu Gu
  • 11
  • 2