How to Distinguish Bool and Int in Swift

Is it possible to distinguish Bool and Int in Swift?

func checkType<T>(value: T) -> String {
var statusText = "not found"
if value is Int {
statusText = "It is an integer"
} else if value is Bool {
statusText = "It is an boolean"
} else if value is String {
statusText = "It is an string"
}
return statusText
}

AnyObject cannot be implicitly downcast to any type in Swift. For such case you can use Generics instead.

Generic code enables you to write flexible, reusable functions and types that can work with any type, subject to requirements that you define. Read more.

Converting Boolean value to Integer value in Swift

Swift 5

Bool -> Int

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

Int -> Bool

extension Int {
var boolValue: Bool {
return self != 0
}
}

Type checks on Int and Bool values are returning incorrectly in Swift 4.2

Sadly, you can't solve this without going down to a pretty low level, using either Core Foundation types or Objective-C @encode strings.

The problem is that, under the covers, Foundation's JSON serialization uses NSNumber to wrap both integers and booleans. So the JSON 0 and the JSON true are both converted to NSNumber objects, and Swift is willing to convert either of those NSNumber objects to an Int or a Bool on request.

However, the JSON booleans are in fact converted to a subclass of NSNumber called __NSCFBoolean, which is the type that a CFBooleanRef (in Swift, CFBoolean) references:

import Foundation

let json = """
{
"number_of_likes": 0,
"is_liked": true

}
"""

let data = json.data(using: .utf8)!
let jso = try! JSONSerialization.jsonObject(with: data, options: []) as! [String: Any]

for key in jso.keys {
if let value = jso[key] as? NSNumber {
print("\(key) \(type(of: value)) \(String(cString: value.objCType))")
}
}

Output:

number_of_likes __NSCFNumber q
is_liked __NSCFBoolean c

It's not documented that Foundation JSON serialization decodes JSON booleans to Core Foundation CFBooleans, but it's unlikely to change.

So here's a Core Foundation way to test:

if let isLikedCF = jso["is_liked"] as CFTypeRef?,
CFGetTypeID(isLikedCF) == CFBooleanGetTypeID()
{
print("it's bool")
} else {
print("it's not bool")
}

What we're doing here is converting the value from the JSON dictionary to a CFTypeRef (which is a reference to any Core Foundation type, and everything returned by Foundation's JSONSerialization is toll-free bridged to a Core Foundation type), and then checking whether the Core Foundation object's type ID is the CFBoolean type ID.

Another way to test using Core Foundation is to recognize that there are only two CFBooleanRef values, kCFBooleanTrue and kCFBooleanFalse. You can see if jso["is_liked"] as? NSNumber is identical to one of those two values, using ===:

if let isLikedNumber = jso["is_liked"] as? NSNumber,
isLikedNumber === kCFBooleanTrue || isLikedNumber === kCFBooleanFalse
{
print("it's bool")
} else {
print("it's not bool")
}

You can also test by checking the NSNumber's Objective-C type code. You cast jso["is_liked"] as? NSNumber, ask for its objCType, convert the resulting C-string to a Swift String, and compare it to "c". If so, it's a boolean. Otherwise, it's not.

if let isLikedNumber = jso["is_liked"] as? NSNumber {
if String(cString: isLikedNumber.objCType) == "c" {
print("it's bool")
} else {
print("it's not bool")
}
}

The c comes from @encode(BOOL) (in Objective-C), where BOOL is a typedef of signed char. This is really obscure stuff. I'd recommend going with a Core Foundation test (shown above), since it's easier to understand and better documented.

Using Swift, how can I tell the difference between a boolean and integer value when loading data from a plist?

I did some more research and decided to use CFPropertyList, which solves my problem as booleans are CFBooleanRefs, not NSNumbers.

If anyone wants the actual code ask in the comments. It's a bit messy right now which is why I am not posting it right away.

Converting Int to Bool

No, there is and has never been an explicit built in option for conversion of Int to Bool, see the language reference for Bool for details.

There exists, still, however, an initializer by NSNumber. The difference is that implicit bridging between Swift numeric type and NSNumber has been removed in Swift 3 (which previously allowed what seemed to be explicit Bool by Int initialization). You could still access this by NSNumber initializer by explicitly performing the conversion from Int to NSNumber:

let number = 1
let result = Bool(number as NSNumber)

print(result) // true

As @Hamish writes in his comment below: if we leave the subject of initializers and just focus on the end result (instantiating a Bool instance given the value of an Int instance) we can simply make use of the != operator for Int values (specifically, the operator with signature func !=(lhs: Int, rhs: Int) -> Bool), a generalization easily achievable using the != operator approach:

let number = -1
let result = number != 0

print(result) // true

Much like you yourself as well as @JAL describes in his answer, you could construct your own Bool by Int initializer, but you might as well consider generalizing this for any type conforming to the Integer protocol:

extension Bool {
init<T: Integer>(_ num: T) {
self.init(num != 0)
}
}

/* example usage */
let num1: Int8 = -1
let num2: Int = 3
let num3: UInt64 = 0
// ....
let result1 = Bool(num1) // true
let result2 = Bool(num2) // true
let result3 = Bool(num3) // false

Change Bool value to Int

I don't think you need to convert a Bool to an Int to get the total score. You could get the score using this simple approach:

struct caneSkills: View {
@State var front = false
@State var behind = false
@State var onTop = false
@State var below = false
@State var between = false
@State var nextTo = false
@State var under = false
@State var onBottom = false
@State var inBack = false
@State var onTheSide = false

// -- here count all true values
var psScore: Int {
[front,behind,onTop,below,between,nextTo,under,onBottom,inBack,onTheSide].filter{ $0 }.count
}

var body: some View {
NavigationView {
Form {
Section(header: Text("Positional/Spatial Concepts")) {
Toggle("Behind", isOn:$behind)
Toggle("Below", isOn:$below)
Toggle("In Back", isOn:$inBack)
Toggle("In Between", isOn:$between)
Toggle("In Front", isOn:$front)
Toggle("Next To", isOn:$nextTo)
Toggle("On The Bottom", isOn:$onBottom)
Toggle("On Side", isOn:$onTheSide)
Toggle("On Top", isOn:$onTop)
Group{
Toggle("Under", isOn:$under)
Text("Positional/Spatial Concepts Score: \(psScore) / 10")
.font(.headline)
.foregroundColor(Color.blue)
}
}
}
.navigationTitle("Cane Skills")
}
.navigationViewStyle(StackNavigationViewStyle())
}
}

Determine that `true` is a Bool and not a number equal to 1

1, 1.0, true, all types are bridged to NSNumber

You can check the objCType

let some : AnyObject = true

if let type = String.fromCString(some.objCType) {
switch type {
case "c" : print("is Bool", some as! Bool)
case "q" : print("is Int", some as! Int)
case "d" : print("is Double", some as! Double)
default : print("no idea")
}
} else {
print("no matching objCType")
}

Source: Type Encodings

Integer vs Boolean array Swift Performance

TL, DR:

  • Do not attempt to optimize your code in a Debug build. Always run it through the Profiler. Int was faster then Bool in Debug but the oposite was true when run through the Profiler.
  • Heap allocation is expensive. Use your memory judiciously. (This question discusses the complications in C, but also applicable to Swift)

Long answer

First, let's refactor your code for easier execution:

func useBoolArray(n: Int) {
var prime = [Bool](repeating: true, count: n+1)
var p = 2

while((p*p)<=n)
{
if(prime[p] == true)
{
var i = p*2
while (i<=n)
{
prime[i] = false
i = i + p
}
}
p = p+1
}
}

func useIntArray(n: Int) {
var prime = [Int](repeating: 1, count: n+1)
var p = 2

while((p*p)<=n)
{
if(prime[p] == 1)
{
var i = p*2
while (i<=n)
{
prime[i] = 0
i = i + p
}
}
p = p+1
}
}

Now, run it in the Debug build:

let count = 100_000_000
let start = DispatchTime.now()

useBoolArray(n: count)
let boolStop = DispatchTime.now()

useIntArray(n: count)
let intStop = DispatchTime.now()

print("Bool array:", Double(boolStop.uptimeNanoseconds - start.uptimeNanoseconds) / Double(NSEC_PER_SEC))
print("Int array:", Double(intStop.uptimeNanoseconds - boolStop.uptimeNanoseconds) / Double(NSEC_PER_SEC))

// Bool array: 70.097249517
// Int array: 8.439799614

So Bool is a lot slower than Int right? Let's run it through the Profiler by pressing Cmd + I and choose the Time Profile template. (Somehow the Profiler wasn't able to separate these functions, probably because they were inlined so I had to run only 1 function per attempt):

let count = 100_000_000
useBoolArray(n: count)
// useIntArray(n: count)

// Bool: 1.15ms
// Int: 2.36ms

Not only they are an order of magnitude faster than Debug but the results are reversed to: Bool is now faster than Int!!! The Profiler doesn't tell us why how so we must go on a witch hunt. Let's check the memory allocation by adding an Allocation instrument:

Bool Array
int Array

Ha! Now the differences are laid bare. The Bool array uses only one-eight as much memory as Int array. Swift array uses the same internals as NSArray so it's allocated on the heap and heap allocation is slow.

When you think even more about it: a Bool value only take up 1 bit, an Int takes 64 bits on a 64-bit machine. Swift may have chosen to represent a Bool with a single byte, while an Int takes 8 bytes, hence the memory ratio. In Debug, this difference may have caused all the difference as the runtime must do all kinds of checks to ensure that it's actually dealing with a Bool value so the Bool array method takes significantly longer.

Moral of the lesson: don't optimize your code in Debug mode. It can be misleading!

Bool being seen as int when using AnyObject

Instead of:

var params = Dictionary<String,AnyObject>()

Try:

var params = Dictionary<String,Any>()

Any can represent an instance of any type at all, including function
types.

Documentation:
https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TypeCasting.html#//apple_ref/doc/uid/TP40014097-CH22-XID_448

In this case it appears you need a Dictionary and the service is expecting "true" as opposed to the bool value.

I recommend creating a function to convert your bool value to a String and using that to set your params["tester"].

Example:

param["tester"] = strFromBool(true)

and then define the function strFromBool to accept a bool parameter and return "true" or "false" depending on its value.

How to check boolean value in integer Swift

You can check if a value is inside an interval with a switch:

func getGrade(score: Int) -> String {
let result: String
switch score {
case 80...100 :
result = "A"
case 75..<80 :
result = "B"
case 60..<75 :
result = "C"
case 50..<60 :
result = "D"
case 40..<50 :
result = "E"
case 0..<40 :
result = "F"
default:
result = "F"
}
return result
}

let mathsStr = "42"
let mathsNum = mathsStr.toInt()

println(getGrade(mathsNum!)) // prints "E"


Related Topics



Leave a reply



Submit