Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift Optional of Optional

For a project of mine, I have to create a proxy AppDelegate that will forward calls to a another AppDelegate.

UIApplicationDelegate havs a var window: UIWindow?. My question is, why can't I do this:

private lazy var realAppDelegate: UIApplicationDelegate = {
    return AppDelegate()
}()

var window: UIWindow? {
    get {
        return realAppDelegate.window
    }
    set {
        realAppDelegate.window = newValue
    }
}

The problem with that code is that realAppDelegate.window is a UIWindow??.

Does anybody know why?

like image 453
delannoyk Avatar asked Jan 08 '23 12:01

delannoyk


2 Answers

The property window of UIApplicationDelegate protocol is declared like this:

optional var window: UIWindow? { get set }

That means that it is an optional property (in the sense that "the class implementing the UIApplicationDelegate protocol is not requested to implement/have this property", like when you have @optional in Objective-C), and that property is of optional type Optional<UIWindow> (or UIWindow?).

That's why you have the double-optional type in the end, because that window property may or may not be implemented in realDelegate, and if it is, it will be itself of type Optional<UIWindow>/UIWindow?.


So basically what you want is to return the window property of your realAppDelegate… only if that realAppDelegate decided to declare that property itself (which it isn't requires to do, as it's optional var).

  • If the realAppDelegate did not implement window itself, you probably intend to return a nil UIWindow? as a result.
  • If your realAppDelegate did actually implement the window property, then you need to return it as is (wether this implementation returns an actual UIWindow or a nil one).

The easiest way to do that is to use the nil-coalescing operator ?? in Swift. a ?? b meaning that "if a is non-nil, then return a, but if a is nil, return b" (where if a is of type T?, then the whole expression is expected to return an object of type T, where in your case T is the type UIWindow?).

var window: UIWindow? {
    get {
        // If realAppDelegate.window (of type UIWindow??) is not implemented
        // then return nil. Otherwise, return its value (of type UIWindow?)
        return realAppDelegate.window ?? nil
        // That code is equivalent (but more concise) to this kind of code:
        //   if let w = realAppDelegate.window { return w } else return nil
    }
    ...
}

To implement the setter, that's another problem. According to this SO answer, directly accessing to the setter of an optional property of a protocol doesn't seem to be possible. But you can imagine a hack to workaround this, by declaring another protocol that makes this window property requirement mandatory, then try to cast to it in the setter:

@objc protocol UIApplicationDelegateWithWindow : UIApplicationDelegate {
    var window: UIWindow? { get set }
}

class AppDelegateWrapper : UIApplicationDelegate {
    ...
    var window: UIWindow? {
        get {
            return realAppDelegate.window ?? nil
        }
        set {
            if let realAppDelWithWindow = realAppDelegate as? UIApplicationDelegateWithWindow
            {
                // Cast succeeded, so the 'window' property exists and is now accessible
                realAppDelWithWindow.window = newValue
            }
        }
    }
...
}
like image 73
AliSoftware Avatar answered Jan 17 '23 06:01

AliSoftware


The declaration of the property is

optional var window: UIWindow? { get set }

The optional in the beginning means that the property doesn't have to be there at all, that's the second ?.

UIApplicationDelegate is a protocol, the class that implements it doesn't have to implement everything.

like image 41
Sulthan Avatar answered Jan 17 '23 06:01

Sulthan