Swift Optionals - Variable binding in a condition requires an initializer - swift

I am new to Swift and trying to figure out the Optional concept. I have a small piece of code in Playground which is giving me "Variable binding in a condition requires an initializer" error. Can someone please explain why and how do I fix it?
I only want to print "Yes" or "No" depending on if "score1" has a value or not. Here is the code:
import Cocoa
class Person {
var score1: Int? = 9
func sum() {
if let score1 {
print("yes")
} else {
print("No")
}
}//end sum
}// end person
var objperson = person()
objperson.sum()

The if let statement takes an optional variable. If it is nil, the else block or nothing is executed. If it has a value, the value is assigned to a different variable as a non-optional type.
So, the following code would output the value of score1 or "No" if there is none:
if let score1Unwrapped = score1
{
print(score1Unwrapped)
}
else
{
print("No")
}
A shorter version of the same would be:
print(score1 ?? "No")
In your case, where you don't actually use the value stored in the optional variable, you can also check if the value is nil:
if score1 != nil {
...
}

Writing
if let score1 {
doesn't make sense. If you want to see if score has a value, use
if score1 != nil {
or
if let score = score1 {
The last case binds a new non-optional constant score to score1. This lets you use score inside the if statement.

The code in your question is similar to something I saw in the swift book and documentation and they are correct.
Your playground is just using an old version of swift which currently doesn't support this syntax. Using a beta version of XCode should fix
https://www.reddit.com/r/swift/comments/vy7jhx/unwrapping_optionals/

the problem is that if let assumes you want to create a constant score1 with some value. If you just want to check if it contains a value, as in not nil you should just do it like below:
if score1! != nil {
// println("yes")
So your full code would look like this:
class Person {
var score1: Int? = 9
func sum() {
if score1 != nil {
println("yes")
}
else {
println("no")
}
}
}
var objperson = Person()
objperson.sum()

You can unwrap it using this:
import Cocoa
class Person {
var score1: Int? = 9
func sum() {
print("\(score1 != nil ? "YES" : "NO")")
}
}
And then call it like:
var objperson = Person()
objperson.sum()

Related

Forwarding function generic parameter to generic class type

I have created enum with associated value and I want to be able to dynamically update associated value. As far as I know Swift doesn't support that at the moment.
Because of that I used following approach:
enum PersonInfo {
class EnumValue<T> {
var value: T
init(_ value: T) {
self.value = value
}
}
// Instead of using String or Bool or any other type directly, use EnumValue wrapper
case firstName(EnumValue<String>)
case lastName(EnumValue<String>)
case isAdult(EnumValue<Bool>)
}
I want to add function that would update EnumValue.value property in following way:
func updateAssociatedValue<V>(_ updateValue: V) {
let mirror = Mirror(reflecting: self)
for associatedValue in mirror.children {
guard let value = associatedValue.value as? EnumValue<V> else {
continue
}
value.value = updateValue
}
}
Problem is that this guard statement always fails (guard let value = associatedValue.value as? EnumValue<V>) and I can't figure it out why.
On the other hand, when I write updateAssociatedValue with typed type then things work properly:
// This works
func updateAssociatedValue(_ updateValue: String) {
let mirror = Mirror(reflecting: self)
for associatedValue in mirror.children {
guard let value = associatedValue.value as? EnumValue<String> else {
continue
}
value.value = updateValue
}
}
Things compile normally but during the runtime guard statement always fails. Am I using generic value in some incorrect way? Should I use somehow updateValue.Type or updateValue.self (I tried but it didn't work).
Example of usage:
var array: [PersonInfo] = [
.firstName(PersonInfo.EnumValue("John")),
.lastName(PersonInfo.EnumValue("Doe")),
.isAdult(PersonInfo.EnumValue(false))
]
print(array)
// John, Doe, false
array.first?.updateAssociatedValue("Mike")
print(array)
// Mike, Doe, false
I can always reassign enum value in array but if possible I want to avoid that. That's the reason for asking this question.

Swift optional binding in while loop

According to Swift documentation:
Optional binding can be used with if and while statements to check for a value inside an optional, and to extract that value into a constant or variable, as part of a single action.
The documentation only shows an example of optional-binding using if statement like:
if let constantName = someOptional {
statements
}
I'm looking for an example of optional-binding using while loop?
It's the same
while let someValue = someOptional
{
doSomethingThatmightAffectSomeOptional(with: someValue)
}
Here is a concrete example of iterating a linked list.
class ListNode
{
var value: String
var next: ListNode?
init(_ value: String, _ tail: ListNode?)
{
self.value = value
self.next = tail
}
}
let list = ListNode("foo", ListNode("bar", nil))
var currentNode: ListNode? = list
while let thisNode = currentNode
{
print(thisNode.value)
currentNode = thisNode.next
}
// prints foo and then bar and then stops
I think the advantage of using while let ... is infinite loop that check some variable for any changes. But it's weird. For this kind of work you should use didSet. Or other good example is List data structure. Anyway, here is the example:
var value: Int? = 2
while let value = value {
print(value) // 2
break
}

Unwrapping Swift optional without variable reassignment

When using optional binding to unwrap a single method call (or optional chaining for a long method call chain), the syntax is clear and understandable:
if let childTitle = theItem.getChildItem()?.getTitle() {
...
}
But, when provided the variable as a parameter, I find myself either using:
func someFunction(childTitle: String?) {
if let theChildTitle = childTitle {
...
}
}
or even just redefining it with the same name:
if let childTitle = childTitle { ... }
And I've started wondering if there is a shortcut or more efficient of performing a nil check for the sole purpose of using an existing variable. I've imagined something like:
if let childTitle { ... }
Does something like this exist, or at least an alternative to my above two interim solutions?
No. You should unwrap your optionals just redefining it with the same name as you mentioned. This way you don't need to create a second var.
func someFunction(childTitle: String?) {
if let childTitle = childTitle {
...
}
}
update: Xcode 7.1.1 • Swift 2.1
You can also use guard as follow:
func someFunction(childTitle: String?) {
guard let childTitle = childTitle else {
return
}
// childTitle it is not nil after the guard statement
print(childTitle)
}
Here's the only alternative I'm aware of.
func someFunction(childTitle: String?) {
if childTitle != nil {
...
}
}
But then childTitle is still an optional, so you would have to unwrap it every time you use it: childTitle!.doSomething(). It shouldn't affect performance, though.
I've gotten into the habit of just reassigning to the same name. That eliminates the need to come up with a separate name and avoids any confusion about whether the new name is supposed to be used anywhere. However, note the output after the block:
var test: String? = "123"
if var test = test {
test += "4"
test += "5"
}
print(test) // prints "123"
If you need to access the modified value after the block, you can assign the variable to a new name and assign the new name back to the old name inside the block:
var test: String? = "123"
if var testWorking = test {
testWorking += "4"
testWorking += "5"
test = testWorking
}
print(test) // prints "12345"

What's wrong with my code that tries to check the type of a variable? [duplicate]

This question already has answers here:
How to determine the type of a variable in Swift
(5 answers)
Closed 8 years ago.
What's wrong with my code that tries to check the type of a variable?
The following code produces error that says "'is' test is always true". Note that I don't want to set p to a value because it could be nil, hence the use of optional.
import Foundation
var p:String?
if p as String? {
println("p is a string type")
}
else {
println("p is not a string type")
}
Now if I test against String type, it won't even compile:
import Foundation
var p:String?
if p as String {
println("p is a string type")
}
else {
println("p is not a string type")
}
Is this a compiler bug? If not what did I do wrong?
Adding on to the answers that revolve around optional binding, there is a more direct way that Apple provides.
The is operator exists exactly for this purpose. However, it doesn't allow you to test trivial cases but rather for subclasses.
You can use is like this:
let a : Any = "string"
if a is String {
println("yes")
}
else {
println("no")
}
As expected, that prints yes.
You already know that p is an optional string. You don't need to convert it to a type, you can simply do Optional Binding:
if let aString = p {
println("p is a string: \(aString)")
}
else {
println("p is nil")
}
Normally you check if a variable is of a certain type using the as? operator:
var something : AnyObject = "Hello"
if let aString = something as? String {
println("something is a string: \(aString)")
}
but you do not use that mechanism when checking if an optional is nil.
This will also work if your object is an optional:
var something : AnyObject? = "Hello"
if let aString = something as? String {
println("something is a string: \(aString)")
}
Since p is an optional String, you can use it in your conditional test like so:
import Foundation
var p: String?
if p {
println("p has been assigned a String value")
}
else {
println("p is nil")
}
If p has been assigned a String value and you would like to use that value in the body of your if statement, then you can use 'optional binding' to assign its value to a local constant you can work with:
if let pValue = p {
println("p is assigned \(pValue)")
}
else {
println("p is nil")
}

How would I create a constant that could be one of several strings depending on conditions?

I want to have a constant using let that may be one of several values.
For instance:
if condition1 {
constant = "hi"
}
else if condition2 {
constant = "hello"
}
else if condition3 {
constant = "hey"
}
else if condition4 {
constant = "greetings"
}
I'm not sure how to do this with Swift and the let feature. But I'm inclined to believe it's possible, as this is in the Swift book:
Use let to make a constant and var to make a variable. The value of a constant doesn’t need to be known at compile time, but you must assign it a value exactly once.
How would I accomplish this?
As pointed out in the other answers you can't directly do this. But if you're looking to just variably set the initial value of a constant, then yes, that is possible. Here's an example with a computed property.
class MyClass {
let aConstant: String = {
if something == true {
return "something"
} else {
return "something else"
}
}()
}
I think you are looking for variable which will be assigned later inside switch-case:
let constant :String
switch conditions {
case condition1:
constant = "hi"
case condition2:
constant = "hello"
case condition3:
constant = "hey"
case condition4:
constant = "greetings"
default:
constant = "salute"
}
One option would be something like this, using a closure:
let constant: String = ({ value in
if conditionOne {
return "Hi"
} else if conditionTwo {
return "Bye"
}
return "Oops!"
})(myData /*needed for condition*/)
Or, for another twist, using generics:
func fancySwitch<S, T>(val: S, fn: S -> T) -> T {
return fn(val)
}
let x: String = fancySwitch(3) { val in
if val == 2 {
return "Hi"
} else if val < 5 {
return "Bye"
}
return "Oops"
}
let y: String = fancySwitch((3, 4)) { (a, b) in
if a == 2 {
return "Hi"
} else if b < 5 {
return "Bye"
}
return "Oops"
}
I understand what you're looking for. In Scala and some other functional languages this can be done using the match statement (kind of like switch) because the entire statement resolves to a value like this:
val b = true
val num = b match {
case true => 1
case false => 0
}
This is unfortunately not directly possible in Swift because there is no way to get a value from a branch statement. As stated in the Swift book, "Swift has two branch statements: an if statement and a switch statement." Neither of these statements resolve to a value.
The closest code structure I can think of is to first use a variable to retrieve the correct value and then assign it to a constant to be used in any later code:
let b = true
var num_mutable: Int
switch b {
case true:
num_mutable = 1
default:
num_mutable = 0
}
let num = num_mutable
Just add the line let constant: String before your if/else statement.
Below, an excerpt from Swift 1.2 and Xcode 6.3 beta - Swift Blog - Apple Developer elaborates.
let constants are now more powerful and consistent — The new rule is
that a let constant must be initialized before use (like a var), and
that it may only be initialized, not reassigned or mutated after
initialization. This enables patterns like:
let x : SomeThing
if condition {
x = foo()
} else {
x = bar()
}
use(x)
This formerly required the use of a var even though there is no
mutation taking place. Properties have been folded into this model to
simplify their semantics in initializers as well.
I found the Swift blog post above from the article "Let It Go: Late Initialization of Let in Swift", which I found by googling: swift let constant conditional initialize.