Andrew Ebling
Andrew Ebling

Reputation: 10283

Convert String to Bool in Swift - via API or most Swift-like approach

Is there an API to convert most possible String representations of Boolean values (e.g. "True", "true", "False", "false", "yes", "no", "1", "0") into a Bool in Swift?

If not, what would be the most Swift-like approach to coding this from scratch? Would it be a functional map() operation? Or something else?

The original source data in this instance is JSON, but I'm interested in the crux of solving the problem in the most Swift-like way possible and hence learning more about the language in the process.

Upvotes: 82

Views: 85882

Answers (16)

Nav Brar
Nav Brar

Reputation: 390

Create a function in String extension and call it from anywhere and no issue with uppercase, lowercase, capitalised or any formatted string.

extension String {
 func toBool() -> Bool? {
    switch self.lowercased() {
      case "true", "yes", "1":
        return true
      case "false", "no", "0":
        return false
      default:
        return nil
    }
  }
}


Usage:
let text = "yES"
let text1 = "FALSE"
let boolValue = text.toBool() // return true
let boolValue = text1.toBool() // return false
let boolValue = "random".toBool() // return nil
let boolValue = "0".toBool() // return false
let boolValue = "1".toBool() // return true

Upvotes: 0

Kirsteins
Kirsteins

Reputation: 27345

There is not built in way AFAIK. Similar method to standard toInt() could be:

extension String {
    var bool: Bool? {
        switch self.lowercased() {
        case "true", "t", "yes", "y":
            return true
        case "false", "f", "no", "n", "":
            return false
        default:
            if let int = Int(self) {
                return int != 0
            }
            return nil
        }
    }
}

Upvotes: 99

Ivan Porkolab
Ivan Porkolab

Reputation: 2009

I think easiest way is:

let someString = "true"
let convertedBool = Bool(someString) ?? false

Upvotes: 8

Uladzimir
Uladzimir

Reputation: 3496

// Bool to String
var bool = true                         // true
var string = String(describing: bool)   // "true"

// String to Bool
bool = Bool(string) ?? false            // true
string = String(describing: bool)       // "true"

Upvotes: 2

Nicolas Manzini
Nicolas Manzini

Reputation: 8546

Typecasting along with a nice String extension and you're up and running

extension String {
var boolValue: Bool {
    return (self as NSString).boolValue
}}

Upvotes: 63

jangelsb
jangelsb

Reputation: 191

I took Nicolas Manzini's solution and modified it slightly. I recommend using the cast operator because it is bridged between String and NSString. It is unknown if it is a toll-free bridge, but it should have better performance than blindly creating a new object.

extension String {

    var boolValue: Bool {
        return (self as NSString).boolValue
    }
}

Upvotes: 8

adamjansch
adamjansch

Reputation: 1381

No other answers deal with the potential of full or mixed caps strings. So why not take Kirsteins answer, mix it with some computed property magic and shazam:

extension String {
    var bool: Bool? {
        let lowercaseSelf = self.lowercased()

        switch lowercaseSelf {
        case "true", "yes", "1":
            return true
        case "false", "no", "0":
            return false
        default:
            return nil
        }
    }
}

Usage would be:

let trueBoolString = "TRUE"   // or "True", "true", "YES", "yEs", and so on
print(trueBoolString.bool)    // true

Upvotes: 6

doovers
doovers

Reputation: 8685

I prefer this implementation that handles optional strings and has a default value of false

extension Bool {
    init(_ string: String?) {
        guard let string = string else { self = false; return }

        switch string.lowercased() {
        case "true", "yes", "1":
            self = true
        default:
            self = false
        }
    }
}

Upvotes: 0

Paco_BB
Paco_BB

Reputation: 9

If you use once, try this.

let str:String = "1"

var boo:Bool = str == "1" || str == "true" || str == "True" || str == "yes"

Upvotes: 0

Chetan Prajapati
Chetan Prajapati

Reputation: 2307

Conclusion from above answers:

You Can right simple one line extension and use.

Swift 3

extension String {
    func boolValueFromString() -> Bool {
        return NSString(string: self).boolValue
    }
}

Usage

 if (dictCardData.value(forKey: "isadmin") as! String).boolValueFromString() {
    // Stuff
}

Upvotes: 3

buxik
buxik

Reputation: 2635

I refactored @Kirsteins code as Bool initializer with String value

extension Bool {

    init?(string: String) {
        switch string {
        case "True", "true", "yes", "1":
            self = true
        case "False", "false", "no", "0":
            self = false
        default:
            return nil
        }
    }

}


// tests
let one = Bool(string: "SDASD") // nil
let two = Bool(string: "0") // false
let three = Bool(string: "true") // true
let four = Bool(string: "null") // nil

Upvotes: 2

Nikolay Shubenkov
Nikolay Shubenkov

Reputation: 3233

one line solution for optional string

let value:String? = "put some string or even just nil here"
let trueOrFalse = NSString(string: value ?? "").boolValue

Upvotes: -1

Alex Milousse
Alex Milousse

Reputation: 139

Use this,

self.boolType = NSString(string:stringType!).boolValue

Upvotes: 13

Duyen-Hoa
Duyen-Hoa

Reputation: 15794

In objective-C, we have boolValue for a String. You can convert your string to NSString then use it, if it doesn't exist in Swift

var aString = NSString(string: "tRue")

var b = aString.boolValue

b should return true if aString.upperCase == "TRUE"

Update: for Info (just seen in apple API):

var boolValue: Bool { get } // Skips initial space characters (whitespaceSet), or optional -/+ sign followed by zeroes. Returns YES on encountering one of "Y", "y", "T", "t", or a digit 1-9. It ignores any trailing characters.

Update 2: I did a performance test of this method with extension method. The performance of this method is impressional. Here is the code of my test, I've called these functions in a GCD thread, using simulator, one after other.

dispatch_async(dispatch_queue_create("com.haduyenhoa.test", nil), {
            self.test1()
            self.test2()
        })


func test1() {
    let testTrue: String = "TRue"
    let testFalse: String = "faLse"

    let testNil: String = "whoops!"

    let begin : NSDate = NSDate()

    NSLog("BEGIN native")
    var testTrueObjC: NSString
    var testFalseObjC : NSString
    var testNilObjC:NSString

    for index in 1...100000 {
        testTrueObjC = NSString(string:testTrue)
         testFalseObjC = NSString(string:testFalse)
        testNilObjC = NSString(string:testNil)

        var b1 = testTrueObjC.boolValue // {Some true}

        var b2 = testFalseObjC.boolValue // {Some false}
        var b3 = testNilObjC.boolValue // nil
    }
    let end : NSDate = NSDate()
    let interval = end.timeIntervalSinceDate(begin)
   NSLog("DURATION native: \(interval)")
}

func test2() {
    let testTrue: String = "TRue"
    let testFalse: String = "faLse"

    let testNil: String = "whoops!"

    let begin : NSDate = NSDate()
    NSLog("BEGIN extension")
    for index in 1...100000 {
        var b1 = testTrue.boolValue() // {Some true}
        var b2 = testFalse.boolValue() // {Some false}
        var b3 = testNil.boolValue() // nil
    }
    let end : NSDate = NSDate()
    let interval = end.timeIntervalSinceDate(begin)
    NSLog("DURATION extension: \(interval)")

}

The console log:

2015-03-12 14:16:23.238 testSwift3[2343:298787] BEGIN native
2015-03-12 14:16:23.543 testSwift3[2343:298787] DURATION native: 0.305041968822479
2015-03-12 14:16:23.543 testSwift3[2343:298787] BEGIN extension
2015-03-12 14:16:35.360 testSwift3[2343:298787] DURATION extension: 11.8166469931602

We can improve the performance of the extension solution by simplify the rule. The performance is depend on how we implement the rule.

Upvotes: 38

Para
Para

Reputation: 3711

As already suggested, I would build an extension to String, listing only the string literals you would like to convert to true and false, keeping a nil case for what doesn't convert (or returning false in that case as well? Your choice!). You probably don't want your method to be case sensitive, by the way.

For example:

extension String {
    func boolValue() -> Bool? {
        let trueValues = ["true", "yes", "1"]
        let falseValues = ["false", "no", "0"]

        let lowerSelf = self.lowercaseString

        if contains(trueValues, lowerSelf) {
            return true
        } else if contains(falseValues, lowerSelf) {
            return false
        } else {
            return nil
        }
    }
}

let testTrue: String = "TRue"
testTrue.boolValue() // {Some true}
let testFalse: String = "faLse"
testFalse.boolValue() // {Some false}
let testNil: String = "whoops!"
testNil.boolValue() // nil

Be careful if you use an NSString object and its boolValue() method, as it returns true if it encounters "Y", "y", "T", "t", or a digit 1-9 (See docs here).

As an example:

let nsString = NSString(string: "tFalse")
nsString.boolValue // true

Upvotes: 9

Muhammad Raza
Muhammad Raza

Reputation: 970

 var st = "false"

 extension String {
   func toBool() -> Bool{
    if self == "false" {
        return false
    }else{
        return true
    }
}
}

 if st.toBool() == false {
     println("its ok")
 }

Upvotes: 2

Related Questions