When accessing UIapplication's
main window it is returned as a UIWindow??
let view = UIApplication.sharedApplication().delegate?.window // view:UIWindow??
Why is it returning as a double optional and what does it mean and if put into a if let
should I add one !
after it?
if let view = UIApplication.sharedApplication().delegate?.window!
My first though was to replace ?
with a !
after delegate but that was not the solution.
Optional chaining is a process for querying and calling properties, methods, and subscripts on an optional that might currently be nil . If the optional contains a value, the property, method, or subscript call succeeds; if the optional is nil , the property, method, or subscript call returns nil .
Optional binding is a mechanism built into Swift to safely unwrap optionals. Since an optional may or may not contain a value, optional binding always has to be conditional. To enable this, conditional statements in Swift support optional binding, which checks if a wrapped value actually exists.
Optional binding stores the value that you're binding in a variable. 2. Optional chaining doesn't allows an entire block of logic to happen the same way every time. 2. Optional binding allows an entire block of logic to happen the same way every time.
Swift lets you override its safety by using the exclamation mark character: ! . If you know that an optional definitely has a value, you can force unwrap it by placing this exclamation mark after it. Please be careful, though: if you try this on a variable that does not have a value, your code will crash.
@matt has the details, but there is a (somewhat horrible, somewhat awesome) workaround. (See edit below, though)
let window = app.delegate?.window??.`self`()
I will leave the understanding of this line of code as an exercise for the reader.
OK, I lie, let's break it down.
app.delegate?.window
OK, so far so good. At this point we have the UIWindow??
that is giving us a headache (and I believe is a bug in Swift disconnect between Swift and Cocoa). We want to collapse it twice. We can do that with optional chaining (?.
), but that unwraps and rewraps, so we're back where we started from. You can double-optional-chain, though, with ??.
which is bizarre, but works.
That's great, but ??
isn't a legal suffix operator. You have to actually chain to something. Well, we want to chain back to itself (i.e. "identity"). The NSObject
protocol gives us an identity method: self
.
self
is a method on NSObject
, but it's also a reserved word in Swift, so the syntax for it is `self`()
And so we get our madness above. Do with it as you will.
Note that since ??.
works, you don't technically need this. You can just accept that view
is UIWindow??
and use ??.
on it like view??.frame
. It's a little noisy, but probably doesn't create any real problems for the few places it should be needed.
(*) I used to think of this as a bug in Swift, but it's not fixable directly by optional chaining. The problem is that there is no optional chaining past window
. So I'm not sure where the right place to fix it is. Swift could allow a postfix-?
to mean "flatten" without requiring chaining, but that feels odd. I guess the right operator would be interrobang delegate?.window‽
:D I'm sure that wouldn't cause any confusion.
EDIT:
Joseph Lord pointed out the better solution (which is very similar to techniques I've been using to avoid trivial if-let, but hadn't thought of this way before):
let window = app.delegate?.window ?? nil // UIWindow?
I agree with him that this is the right answer.
It's because the window
property is itself in doubt (it's optional). Thus, you need one question mark because there might or might not be a window property, and another question mark because the return value of that window property is itself an Optional. Thus we get a double-wrapped Optional (as I explain in my tutorial: scroll down to the Tip box where I talk about what happens when an optional property has an Optional value).
Thus, one way to express this would be in two stages — one to cast (and unwrap that Optional), and one to fetch the window (and unwrap that Optional):
if let del = UIApplication.sharedApplication().delegate as? AppDelegate { if let view = del.window {
Now view
is a UIWindow.
Of course, if you're sure of your ground (which you probably are), you can force the cast in the first line and the unwrapping in the second line. So, in Swift 1.2:
let del = UIApplication.sharedApplication().delegate as! AppDelegate let view = del.window!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With