I want to know about Bool in Swift.
If Bool is a basic primitive datatype, why is a Boolean's default value nil?
var test: Bool!
print(test) // nil
In Java the Boolean default value is false:
Default value of 'boolean' and 'Boolean' in Java
Bool, Bool! and Bool? all are different in Swift.
1. Bool is a non-optional data type that can have values - true/false. You need to initialize it in the initializer or while declaring it before using it.
var x : Bool = false
var x: Bool
init()
{
x = false
}
2. Bool? is an optional data type that can have values - nil/true/false. In order to use this type, you need to unwrap it using if let or force unwrapping.
var x: Bool?
if let value = x
{
//TODO: use value instead of x
}
3. Bool! is an implicitly unwrapped optional data type that can have values - nil/true/false. The difference here is it must contain a value before using it else it will result in runtime exception. Since it is implicitly unwrapped, no need to unwrap it using if let or force unwrapping.
var x: Bool! //Must contain value before using
Strictly spoken there is no default value in Swift.
Either the Bool is non-optional then you have to assign a (default) value
or if the Bool is an optional, then it is nil – which is no value in terms of Swift.
Bool in Swift is not a primitive. Everything in Swift are objects. Your variable test is a Bool!, which is an implicitly unwrapped optional and the default value is nil.
If you use this code (not an optional),
var test: Bool
print(test)
You will get an error:
variable 'test' used before being initialized
So in Swift you have to initialize stuff before you use it, for example:
var test: Bool = false
Related
Does it mean that the set operation doesn't read the actual value of the Optional, so it doesn't need to be unwrapped?
var str = "Hello, playground"
class Base{
var name:String?
};
var obj = Base()
obj.name = "hello" //this line don't need unwrapping first
An optional is a box. The box can either contain nothing (which is called nil) or it can contain something of a specific type (a String in your example). You unwrap an optional to access the value inside in the box.
When you are assigning a value to an optional, you just need to assign the value to the box itself. There is no need to unwrap anything, because you are just putting a value into the box. Swift either empties the box, if you assign nil, or it wraps the value by putting it into the box.
Unwrapping is for accessing a value that is already in the box.
Answering your question from a comment on another answer...
But why Optional binding don't need unwrapping? i think i if let constantName = some Optional is kind of assignment too
Optional binding is an unwrapping operation and an assignment operation. It says "if there is a value inside the box, assign it to this new variable and enter the then clause, otherwise proceed to the else clause if it exists".
var optionalValue: String? = "hello"
if let value = optionalValue {
// value is the contents of the box, it has type String
print("value is \(value)")
} else {
// the optional binding failed because the box is empty
print("optionalValue is nil")
}
When you set an Optional property, you do not have to unwrap it.
Unwrapping is required only when assigning an optional value to a non-optional property:
var name: String = "" // non-optional
var str: String?
// this will not compile, because you need to unwrap str first
name = str
// this will compile, because we're providing a default value
name = str ?? ""
// this will also compile, because name is not an optional
// it would still compile if name was optional, because str is optional too
str = name
I am trying to check if the string is empty of not, if it is not empty, i want to display the at the textField (outUrl.text)
let storage = UserDefaults.standard
var mainsite = storage.object(forKey:"sitestring") ?? ""
if (mainsite != "") {
outUrl.text = mainsite
}
But I have error:
Error: != cannot be applied to operands of type string and any
How do i check if the string is not empty?
The recommended solution is to use the appropriate API (string(forKey:) which returns String rather than Any and optional bindings.
The benefit is you get rid of any type cast and the nil coalescing operator
if let mainsite = UserDefaults.standard.string(forKey:"sitestring"), !mainsite.isEmpty {
outUrl.text = mainsite
}
The result returned by the Objective-C API -[UserDefaults obejectForKey:] is of type id, in order to accomodate various types of objects (NSString, NSNumber, etc, etc.). When accessed form Swift code, this becomes Any? (any type, perhaps nil).
If you know the stored value is indeed a string, you need to attempt cast it before comparing: != between values of types String and Any? isn't defined by default! What meaning would that have?):
var mainsite = storage.object(forKey:"sitestring") as? String ?? ""
This way, you either get a String value (if the cast succeeds) or the empty string (if it fails, because either there is no object for key "sitestring" or it isn't convertible to a String value).
The result from UserDefaults.standard is of type Any?, so before assigning the value to mainsite variable, typecast it to String. Here is an example,
let storage = UserDefaults.standard
var mainsite = storage.object(forKey:"sitestring") as? String
Hello I have an optional variable which uses the willset functionality. Since its an optional the regular set will not work. inside the will set I am setting the variable to at least initialize it if the new value is nil. unfortunately, the value after is nil as well any ideas? Thank you
Code:
var arrayOfString: [String]? {
willSet {
self.arrayOfString = newValue == nil ? [String]() : newValue
}
}
Since its an optional the regular set will not work
Not correct. Since it's not a computed property set will not work.
You need to reread the section about willSet and didSet. willSet is called before any value is written to the variable. I.e. whatever you write to the variable inside willSet will be overwritten immediately by the assignment that invokes your code.
If you assign nil you will overwrite your string array with nil.
Simplify by just doing
var arrayOfString: [String] = []
You need to use didSet:
var arrayOfString: [String]? {
didSet {
self.arrayOfString = arrayOfString ?? []
}
}
Otherwise the value is immediately overwritten by the original value.
However note that if the variable should never contain an optional, it should be declared as such:
var arrayOfString: [String]
and the assignment should add the ?? [] when needed.
Otherwise you will have to handle unwrapping everytime you access the value anyway.
class C{
var b:[String]?
subscript(i:Int)->String?{
get{
return b?[i]
}
set{
b?[i] = newValue! // notice the unwrapped “!” here
}
}
}
In the code, I put a exclamation mark "!" to unwrap the newValue,
Because newValue is the same type of the return value of subscript which is String?.
If I don't put the exclamation mark "!" it will report error:
"
error: cannot assign value of type 'String?' to type 'String' b?[i] = newValue
"
Question: b?[i] is obviously an optional String value String?, how come it is of type String.
I wonder it was a bad error code
Your var b: [String]? is an optional array. However the string inside is not optional. If you changed it to: var b: [String?]? then it would work.
You property b is indeed an optional array, but its member elements are not optionals. Hence, when attempting to assign a new value to an existing member, the new value must be of concrete type String, and not the optional type String?.
// this is an Optional array, with non-optional elements
var foo: [String]? = ["foo"]
let optStr: String? = "bar"
let nonOptStr = "bar"
// ok, nonOptStr is not an optional
foo?[0] = nonOptStr
// as for 'nonOptStr', we need to unwrap
// it prior to attempting to assign it as a
// replacement member of 'foo'
if let str = optStr {
foo?[0] = str
}
Also, avoid explicit unwrapping, and consider validating the index prior to using it. E.g.:
class C {
var b: [String]?
subscript(i: Int) -> String? {
get {
if b?.indices.contains(i) ?? false { return b?[i] }
return nil
}
set {
if b?.indices.contains(i) ?? false, let newValue = newValue {
b?[i] = newValue
}
}
}
}
More generally, I found myself with this exact error after doing a split on a string and ended up printing out both variables. That clarified that I was trying to assign a two element variable to an indexed array that contained a string. So, that is the source of the error, which is essentially saying your trying to assign two elements of an array into a string inside your other array. Well, that makes sense out of the error.
Algorithmically it is:
1) array1[0] = array2Item1, array2Item2
That's why this was fine:
2) array1 = array2Item1, array2Item2
but the above was not.
So, the correct thing to do is 2) and then assign it by index:
array1 = array2Item1, array2Item2
array3 = array1[0] to do what I thought I was doing in 1).
Just another explanation for any others who venture here.
I'm reading Apple's developer documentation on Optional Binding
Why can't I use:
if someOptional? {
statements
}
Instead of
if let constantName = someOptional {
statements
}
Why when there is no need for a local variable or constant?
Why can't I use: if someOptional? {...}
Because the ? suffix on an optional variable is reserved for optional chaining, which allows you to access a property or call a method on a given optional variable. For example:
// returns an optional version of a given property
let aProperty = anOptional?.someProperty
// calls the method if aProperty contains a value – otherwise doesn't
aProperty?.doSomething()
If you just want to check whether an optional contains a value, but don't care about that underlying value, you can simply compare it with nil. For example:
if anOptional != nil {
// do something
}
The simple answer is that someOptional is an Optional, whereas constantName is a Type.
An Optional is not simply the state of a variable, it's an entirely different type. If you were to set var someOptional: String? to var someOptional: String, you're not unwrapping someOptional, you're actually changing the type of someOptional.
It's functionally the same as changing var someOptional: String to var someOptional: Int