Swift Optional of Optional

后端 未结 2 563
孤独总比滥情好
孤独总比滥情好 2020-12-11 09:00

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

相关标签:
2条回答
  • 2020-12-11 09:42

    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.

    0 讨论(0)
  • 2020-12-11 09:45

    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
                }
            }
        }
    ...
    }
    
    0 讨论(0)
提交回复
热议问题