43

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.

Arbitur
  • 38,684
  • 22
  • 91
  • 128
  • 1
    It's not: `optional var window: UIWindow? { get set }` – your `delegate` is optional-chained though (using `?`), which results in another level of optionals. – The Paramagnetic Croissant Mar 06 '15 at 15:29
  • But even when i put ! to the right of delegate instead, it still gives ?? – Arbitur Mar 06 '15 at 15:41
  • Similar question here: http://stackoverflow.com/questions/25175544/uiwindow-does-not-have-member-named-bounds. – Martin R Mar 06 '15 at 15:42
  • Thanks @MartinR in if let I added ?? at the end of the line and now it makes view non optional inside the curly braces. – Arbitur Mar 06 '15 at 16:00
  • One `?` for optional delegate variable implementation, and another `?` for variable of optional type. – kelin Mar 21 '17 at 22:24

4 Answers4

36

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

yerforkferchips
  • 1,965
  • 1
  • 19
  • 27
Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • well, `?.` is `flatMap`, so I'm not sure it fixes it. – Rob Napier Mar 06 '15 at 16:18
  • But still, that doesn't compile (Swift 1.1) in a Playground. Probably because it doesn't know if it should return an `UIWindow??` or an `UIWindow?` (both would be valid) – DeFrenZ Mar 07 '15 at 15:44
  • And if you don't want optional: `let window = (UIApplication.sharedApplication().delegate!.window ?? nil)! as UIWindow ` – William Entriken Dec 27 '15 at 22:27
19

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!
matt
  • 515,959
  • 87
  • 875
  • 1,141
  • 2
    At least in 6.3, `delegate` is of type `UIApplicationDelegate?`. Optional chaining should get rid of that extra layer of optional. – Rob Napier Mar 06 '15 at 15:30
  • 1
    @RobNapier Re-expressed. - I've _never_ liked this particular double optional. Swift goes a little far here, in my view. – matt Mar 06 '15 at 15:40
  • 1
    Agreed. But it would be fixed if optional-chaining supported objc-protocol-optional (which I consider a bug that it does not). – Rob Napier Mar 06 '15 at 15:41
  • @RobNapier Agreed (with your bug). – matt Mar 06 '15 at 15:42
  • Note in 1.2, you can also use multi-if-let rather than force-unwrapping (or nested if-let). I just always hate to use `!` unless it dramatically improves things. – Rob Napier Mar 06 '15 at 15:43
  • 1
    @RobNapier Yes, I know about the 1.2 syntax. I'm not convinced that it's better, though. All it does for you, really, is prevent a bunch of right-curly-braces at the end. I prefer the "pyramid of doom"... But the OP can see my discussion [here](http://www.apeth.com/swiftBook/ch05.html#_conditional_binding) if he's curious. – matt Mar 06 '15 at 15:45
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.

Dan Rosenstark
  • 68,471
  • 58
  • 283
  • 421
0

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
}
user1317655
  • 181
  • 1
  • 2