Swift - Left hand side operand with question mark - swift

var i: Int?
i? = 1
print(i)
I thought the above code would print 1 but actually it printed nil. As I guess, ? makes program skip i? = 1 when i is nil.
Even if I replace i? = 1 with i? += 1, it still can run and print nil
var i: Int?
i? += 1
print(i)
i? is Int? and 1 is Int but somehow + operator still works on 2 different types and passed compiler check.
Could anyone help me explain what happened? It's perfect if we can have an official document which talks about this behavior.

This is Optional Chaining as in foo?.bar = 123.
If you change the type to Int, the compiler will complain:
var i: Int
i? = 1 // Cannot use optional chaining on non-optional value of type 'Int'
From the linked reference above:
The assignment is part of the optional chaining, which means none of the code on the right-hand side of the = operator is evaluated.

You can find more info on Swift basics here.
In your example, i is an optional Int. To assign a value to it, you
var i: Int?
i = 1
If you want to calculate a new value for it, you could use optional binding using guard let of if let (the x variable in the example below) or force unwrap, as in the print() statement.
var i: Int?
i = 1
if let x = i {
i = x + 1
}
print(i!)

Related

Why can't I call Optional methods on an optional-chained type?

Consider the following example where foo is explicitly defined as optional:
let foo: Int? = 10
let bar = foo.map { $0 * 2 }
This compiles and works as expected. Now consider a subsequent example using optional chaining:
let foo: [Int]? = [0, 1, 2]
let bar = foo?.count.map { $0 * 2 }
This fails to compile with the following message:
error: value of type 'Int' has no member 'map'
Why does the compiler see foo?.count as an Int and not an Int?? This defies the Swift Language Book:
To reflect the fact that optional chaining can be called on a nil value, the result of an optional chaining call is always an optional value, even if the property, method, or subscript you are querying returns a non-optional value.
This is an operator precedence issue. You need to add parentheses to change the order:
let bar = (foo?.count).map { $0 * 2 }
Consider the case of a Collection like String rather than an Int:
let foo: String? = "ABC"
let bar = foo?.uppercased().map(\.isLowercase) // [false, false, false]
In this case, it's sensible that the .map applies to the String rather than to the Optional. If the precedence were the other way, then you'd need a ?. at every step (or a lot of parentheses :D)
The type of count unwraps to Int if available. Int does not implement the function map. Optional chaining simply fails when it finds nil. The compiler is trying to tell you if it does not fail the operation map cannot happen. The result is bar which is indeed an optional, because if the operation fails( because something unwraps to nil) the entire chain must return nil.

Type conversion needs unwrap?

var a: String = "1"
var b: Int = Int(a)
The example above triggers an error saying that Int needs to be unwrapped.
var a: String = "1"
var b = Int(a)
However if we dismiss the type when declared b and do the same thing, it won't trigger any error.
What is the difference between the two approach? Why the first one needs to be unwrap even though it is not declared as optional?
It is because in first example you are saying that b is of type Int and in second example since you did not make type explicit compiler sets it to Int?.
It is because Int(string) will only work if string can be interpreted as Int, so Int("3")->3 but what should it do if you say Int("text")->nil, because it is not able to parse string into a an Int
You could provide a default value if you wanted in the first example and then it would be OK.
var b: Int = Int(a) ?? 0

Why would you use an optional type for a constant with value?

Have a look at this statement from Apple document:
let optionalInt: Int? = 9
Why would you use Int? as the type for this constant? You know that it cannot be nil as you are assigning the value 9 to it? The purpose of an optional type (as I understand) is to be able to hold nil. There is no need for optionalInt to hold nil in this statement. Could someone explain?
I found a case where you might use it. It's a bit of a stretch, but here we go.
One reason for using let x: Int? = whatever is if you have an overloaded function where the parameters are different types only insofar as one is an optional. For example:
func doSomething(x: Int)
{
print("\(x) is an Int")
}
func doSomething(x: Int?)
{
print("\(x) is an optional")
}
If you want to ensure the second overload is called, you need to make the parameter explicitly an optional e.g.
let y = 5
doSomething(y) // prints 5 is an Int
let z: Int? = 6
doSomething(z) // prints Optional(6) is an optional
I've yet to see anything like this in real life.
As regards the Apple documentation, it's just a simple example.
I think this particular part of the Apples document is intended to simply illustrate the concept of optionals and nothing else. They probably use let out of the good practice to always start with declaring let properties/variables and only switch them to var if there is an explicit need to change their values.
As to the question why would one declare let foo: Int? = 9 and not just let foo = 9. Well, I really can not imagine where it would be needed. Even if some API expects an Int? then you still can pass a variable of Int type to such API and Swift will implicitly convert it for you. Perhaps, if you pass it to a generic function and you want the Int? version of that generic to be used then you will want to do that, but it's way too academic already to have practical use.
A couple ideas:
Assuming you have a method
func foo(xValue: Int?)->Int? {
// some logic
}
Perhaps you want to define your constant to exactly match the method definition.
let optionalInt: Int? = 9
let fooVal = foo(optionalInt)
Or (changing your initial let statement), the constant's value is determined by a function that returns an Optional
let optionalInt: Int? = foo(nil)

Any type in swift and conversion to Int

I've a placeholder object, basically in context of my application, the function carries a playload from one place to the other. I've assigned it the type Any but it's ending up in error
'Any' is not convertible to Int
I'm using Any instead of AnyObject as it can carry non object as well like tuples or closures. Any advise in the following case?
var n: Any = 4
n = n * 4
In a strongly typed language it is important to get your types right as early as possible. Otherwise you'll end up fighting the type system at every turn.
In your case, the placeholder, as you've described, needs to be Any because it is carrying arbitrary data - but n is an Int and should be declared as such.
Your code should have this feel:
func processPlaceholder (placeHolder: Any) {
if let n = placeHolder as? Int {
n = n * 4
// ...
}
else if let s = placeHolder as? String {
s = s + "Four"
// ....
}
// ...
}
If you know n is an Int you can cast it as such.
var n: Any = 4
n = n as Int * 4
n will still be of type Any, so if you want to do multiple operations you will have to cast it each time.
The other answers are perfectly right. This is just to clarify.
Yes, it's an Int under the hood. But not as far as the compiler is concerned! You have typed this thing as an Any and that's all the compiler knows. And you cannot do arithmetic operations with an Any.
If you insist on storing this thing in an Any, then you must cast down to an Int in order to use it as if it were an Int. Now the compiler will let you treat it as an Int. And it will work, too, as long as it really is an Int. (If you lie and it is not an Int, you'll crash when the program runs.)

Do you need to declare datatype of a variable in Swift?

In Swift Programming, should we need to declare datatype of the variable or will the variable change it's type based on value?
Which one is enough to declare a variable:
var MyVar: Int = 50
Or:
var Myvar = 50
var myVar: Int = 50
Or:
var myVar = 50
They are absolutely equivalent. The : Int is unnecessary because you are assigning 50 right there in the declaration, so Swift infers that this is to be an Int variable.
If you are going to assign as part of the declaration, the only time you need to supply the type is when the type is surprising. For example:
var myChar : Character = "s"
If you don't say Character, you'll get a String.
WWDC 2104 - Introduction To Swift (Session 402)
var languageName = “Swift” // inferred as String
var version = 1.0 // inferred as Double
var introduced = 2014 // inferred as Int
var isAwesome = true // inferred as Bool
Also…
var 🐸 = “Frog” // using unicode names
You should also use “let” for variables that will not change.
Type inference is one of the great parts of Swift. This means that when you assign a variable or constant, the system infers what type it should be. If for instance, you were declaring a variable before you set it, you'd need to specify its type explicitly.
var someVar: Int
someVar = 15
That way the compiler knows when it is being properly set.
You can declare a variable implicitly as:
var myVar = 50
or explicitly as:
var myVar:Int = 50
Also notice that you do not use the semi-colon ; to end the line.
Note that values are never implicitly converted to another type. If you need to convert a value to a different type, explicitly make an instance of the desired type, such as the following:
let label = "The width is "
let width = 94
let widthLabel = label + String(width)
From a complete beginner's view (mine) i see the following benefit of declaring a variable explicitly - the compiler will validate the input value - i.e. if you declare an empty variable and then update it with the wrong type value you will be prompted.
The first seems to be used only integer assignment.
I need to write code to test.
Update:
I mean,
Defined as int can not assign to string, the following is wrong:
var myVar: Int
myVar = "50"
And such is wrong:
var myVar
myVar = "50"
So, if assignment when you define a variable, use both two.
Otherwise, use:
var myVar: Int
Therefore, it is no difference between the two lists you.
Note that I changed MyVar to myVar to follow the convention.
var myVar: Int = 50
means -> message to the compiler will be.
Declare a variable called myVar that is of type(:) Int
and
var myVar = 50
will result the exact same. But here compiler will deduce the type for us. (Welcome to modern intelligent compilers). Type inference is strong feature of swift language.
Be aware that Swift is Type Safe Language