Search code examples
ioscocoa-touchappdelegateobject-graph

Is extending UIViewController to access the app delegate an antipattern?


I'm using an API that recommends keeping its client in the app delegate and accessing it through there. If I extended UIViewController to make it easier to access the app delegate, is this any way an antipattern?

extension UIViewController {
    var appDelegate: AppDelegate {
        return UIApplication.shared.delegate as! AppDelegate
    }
}

class SomeViewController: UIViewController {
    ...
    appDelegate.someClient.someMethod()
    ...
}

By antipattern I mean that is it overkill to extend the entire UIViewController class for this simple convenience? Are there any negative impacts, overall, of extending classes like this? Does every view controller, even if it doesn't access the API, implicitly point to the app delegate now?


Solution

  • If I extended UIViewController to make it easier to access the app delegate, is this any way an antipattern?

    extension UIViewController {
        var appDelegate: AppDelegate {
            return UIApplication.shared.delegate as! AppDelegate
        } 
    }
    

    No; quite the contrary. It is quite common to need to access the app delegate and cast it to its actual class so as to be able to access a property or call a method in the app delegate. If this might need to be done in multiple places, a computed property is the standard notation for providing a shorthand. (There is no need to use an extension for this purpose, but there's no harm in doing so, and there may be a notational advantage; for example, the extension can be located in the app delegate file.)


    In a comment, you muse about the wisdom of extending UIViewController. I imagine you have, say, two view controllers that need to access the app delegate, and many others that do not.

    Now, in the first place, that doesn't really change my answer. I can't see what harm it would do or how it is in any way an antipattern to give all your view controllers the ability to access the app delegate via a shortcut even if many of them never actually do so. All view controllers have the ability already to do lots of things that most of them will never actually do.

    But let's say you feel otherwise. Then you could just implement appDelegate explicitly in each relevant view controller (at the cost of violating DRY).

    Or (here's the cool part) you could declare a protocol and a protocol extension, and have only the relevant view controllers adopt it. So, here's your AppDelegate.swift file:

    import UIKit
    
    protocol AppDelegateAccesser {}
    extension AppDelegateAccesser {
        var appDelegate: AppDelegate {
            return UIApplication.shared.delegate as! AppDelegate
        }
    }
    
    @UIApplicationMain
    class AppDelegate: UIResponder, UIApplicationDelegate {
        // ....
    }
    

    Now let's say that only the MyViewController class actually needs to access the app delegate. Then you just say

    extension MyViewController : AppDelegateAccesser {}
    

    This injects appDelegate into MyViewController but no other class. So you would just do that for each view controller that needs to access the app delegate, and no others. I think that's a totally unnecessary exercise, as opposed to your original proposed solution, but it does solve the problem of injecting code only into some classes.


    NOTE: In Swift 5 it will be legal to declare that only a UIViewController subclass may adopt this protocol. You might like that.