1

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?

lurning too koad
  • 2,698
  • 1
  • 17
  • 47
  • _"an API that recommends keeping its client in the app delegate"_ Sounds like a bad practice is already being foisted upon you... – jscs Feb 05 '19 at 17:42
  • @JoshCaswell I don't see why. That's the right way to persist anything that needs reliably to persist throughout the life of the app. CLLocationManager and CMMotionManager are common use cases. – matt Feb 05 '19 at 18:22

2 Answers2

2

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.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Just to give an example, what you're doing would be a right way to store and access a single CLLocationManager in the app delegate, something that is an extremely frequent need in iOS apps. – matt Feb 05 '19 at 17:49
  • And by extending the entire `UIViewController` class, it doesn't create a situation where every view controller, even if it doesn't access the API, implicitly points to the app delegate? – lurning too koad Feb 05 '19 at 17:53
  • It does create that situation but what harm does that do? However, if you have multiple but not all view controllers that must perform this access, then instead just use a protocol and a protocol extension and have just the relevant view controllers adopt that protocol. I'll add that to my answer. – matt Feb 05 '19 at 18:09
0

Put this function to AppDelegate file so you can access AppDelegate throughout your project.

class func shared() -> AppDelegate {
     return UIApplication.shared.delegate as! AppDelegate
}

You can use like :

AppDelegate.shared()
Viral Savaliya
  • 180
  • 3
  • 5
  • I am upvoting. Not because the solution with controller extension is wrong per se but this is just a better solution. – Sulthan Apr 17 '19 at 08:16