Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift Optionals - Inconsistency?

Tags:

ios

swift

I'm slightly confused - I thought I understood Optionals and on the Apple dev forums Chris L, mentioned a work around to the immutable Optional issue was to create a class wrapper for the optional value type. -link!

However take this example with UIWindow (an optional class type with various properties)

The properties still don't seem mutable with optional chaining!

var window: UIWindow?

//this works (force unwrap)
self.window!.backgroundColor = UIColor.greenColor()


//this optional chain doesn't work... why not? Isn't this just a safer version of the above?
self.window?.backgroundColor = UIColor.redColor()

This seems to be fixed in beta 5!

like image 234
Woodstock Avatar asked Jul 09 '14 10:07

Woodstock


2 Answers

update for Xcode beta 5


the originally asked issue has been resolved in Xcode beta5, that may invalidate this answer.


original anwer


that may request further explanation, why it is definitely not inconsistent behaviour but is simple invalid.

the optional value has to be always of the right side of the actual operand, and it cannot be on the left side of it.

see that logic via two simple examples:

example 1

in case of this line of code:

self.window?.backgroundColor = UIColor.redColor()
                      < LEFT ^ RIGHT >

the optional is on the left side which would mean the left side could be nil therefore the following operand would appear here in runtime:

nil = UIColor.redColor()

which is clearly invalid on every level without any further or complex explanation – a nil cannot be assigned to something else, that is why the compiler does not allow it.

NOTE: you may assume that the logical behaviour would be like this in the case of self.window = nil:

nil.backgroundColor = UIColor.redColor()

but the documentation about Optional Chaining highlights a very important behaviour which explains why that is not happening at all:

Multiple queries can be chained together, and the entire chain fails gracefully if any link in the chain is nil.

emphasis is on the word "entire", thus that is why the left side will be nil = ... and not nil.backgroundColor = ... as you'd expect after Objective-C.

example 2

the other answer highligths another idea of how it can be solved:

self.window?.setBackgroundColor(UIColor.redColor())

why is that working? would it not be a little inconsistency here? definitely not.

the actual optional is on the right side of the operand here, because that line is equal to this line, however we are not bothered to get the void in practice at all.

let result: Void! = self.window?.setBackgroundColor(UIColor.redColor())
           < LEFT ^ RIGHT >

as you see there is not any inconsistency at all here, because in the case of self.window = nil that line would be equal to this one in runtime (please see the explanation above):

let result: Void! = nil

and that would be a completely legal operand.

the logic is simple, the optional must be always at the right side of the operand (or operator), on the left side it can be non-optional value only.

like image 200
holex Avatar answered Oct 13 '22 23:10

holex


Optional chaining works for reading values (return value or nil), it works for calling methods (call this method or do nothing) but doesn't work for assignments.

I believe this is by design but I don't see a reason why it is so because this is essentially the same as:

extension UIWindow {
    func setBackgroundColor(color: UIColor) {
        self.backgroundColor = color
    }
}

self.window?.setBackgroundColor(UIColor.redColor())

and that works without problems. You can report a bug or ask on the dev forums. I believe optional chaining here is logically inconsistent.

Your example from dev forums doesn't have anything to do with this problem since that problem is about value types.

like image 33
Sulthan Avatar answered Oct 13 '22 22:10

Sulthan