ios extension Why is main window of type double optional?




double click to minimize window mac sierra (4)

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.


Answer #1

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!

Answer #2

Oh the double optional! Sometimes you can use a double-bang (two exclamation marks) but you cannot cast that way with optional binding. So... my remix of all the other code gets you a UIWindow object called window of the non-optional kind:

guard let w = UIApplication.shared.delegate?.window, let window = w else { return }

But let's not waste time and just use

let window = UIApplication.shared.delegate!.window!!

and be done.


Answer #3

With advent of Swift2 for me a usual workaround in this kind of cases is

if let _window = UIApplication.sharedApplication().delegate?.window, window = _window {
    // Some code... i.e.
    let frame = window.frame
}

Answer #4

@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.





uiwindow