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 CFBoolean
s, 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 CFBooleanRef
s, not NSNumber
s.
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 thenBool
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:
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
Enum of String Type Vs Struct with Static Constant
iOS Swift, Aws Cognito User Pool, Unable to Refresh Access Token
Borders Not Covering Background
How to Completely Remove All Xcode Program and Cache Files
Check If Class Has a Value for a Key
Where Do I Register a Valuetransformer in Swift
Uidatepicker Show Only Sunday's Date Only
How to Get Reliable Timing for My Audio App
How to Create a Pulse Effect on an Skspritenode
How to Fix Cannot Find 'Firebaseapp' in Scope
How to Create an iOS Liveview in Xcode 8/Swift 3
How to Add Two Generic Values in Swift
How to Byte Reverse Nsdata Output in Swift The Littleendian Way
Swift Lose Precision in Decimal Formatting