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?
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.
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()
Related
Right now, I'm trying to use a class from Git called Insomnia to prevent the device from locking while it is charging. The project is here if you're curious. For the example code, it has me doing this:
final class AppDelegate: UIResponder, UIApplicationDelegate {
private let insomnia = Insomnia(mode: .whenCharging)
//app delegate code
}
As you can see, it has you declare the AppDelegate as final so that the insomnia variable isn't deallocated, which would stop it from working. However, I wasn't sure, is this bad practice to declare my AppDelegate as final? Or will this cause issues for my app? If it is bad practice, is there a better way of making sure insomnia isn't deallocated?
You say:
As you can see, it has you declare the AppDelegate as final so that the insomnia variable isn't deallocated ...
That’s not what final does. It merely says that the class cannot be subclassed (which is not likely to ever be relevant in the case of an app delegate). The final keyword also permits some optimizations (where if you had code calling any of these methods, it could use static dispatch instead of dynamic dispatch) that are unlikely to have any observable effect in this scenario.
Bottom line, you do not have to use final in this context.
"Best practice" would be to use final where you need to, namely where the class really cannot or should not be subclassed or where you really need the performance difference that static dispatch offers.
In answer to your question whether this is an acceptable place to declare insomnia, yes it is (but no final needed). The AppDelegate is not released while the app is running and it's where we generally put "app lifecycle" related code.
It may look a dumb question (sorry if it is), but usually, in my apps, I have this rootVC to inherit with others VCs. So came to me that I have multiples instances of this root. For example, in AppDelegate I call my first view as:
let window = UIWindow(frame: UIScreen.main.bounds)
let root = SplashViewController(nibName: "SplashViewController", bundle: nil)
window.rootViewController = UINavigationController(rootViewController: root)
window.makeKeyAndVisible()
self.window = window
Then SplashViewController inherits from my RootViewController where I can make some view configurations. But, when I call another VC (like InitialViewController) which also inherit from my root, I'm creating new instance from my root or using the same?
And do you think that it is a good practice?
I was reading and searching but I couldn't find or understand clearly in the api reference: https://developer.apple.com/reference/uikit/uiviewcontroller
Any Suggestion? Thanks in advance!
Having a common subclass for all your view controllers could be useful, but try not to burden it too much. Also consider using composition instead of inheritance where possible and reasonable (Prefer composition over inheritance?).
Then SplashViewController inherits from my RootViewController where I can make some view configurations. But, when I call another VC (like InitialViewController) which also inherit from my root, I'm creating new instance from my root or using the same?
Don't worry. If you don't use static variables or variables the in global scope, then each instance of your view controllers will be independent.
This is an example falling into the anti patterns of OOPS. In Swift you can address this with the use of protocols.
Say, you have a common class to inherit , what if some set of class needs a special functionality which others don't?. If you choose to add this in the common superclass you tend to bloat all other subclasses that inherit the super class. If you don't, you may need to duplicate the code.
You can approach the problem elegantly with the use of protocols, I think this how you would do,
protocol CommonTraits { }
extension CommonTraits { // provide default implementations of common methods }
protocol SpecialSetOfTraits { }
extension SpecialSetOfTraits { // provide default implementations of common methods }
Class A : CommonTraits {} // less bloating
Class B : CommonTraits, SpecialSetOfTraits { } // Additional functionality.
This is why i love Swift :)
I have a variable declared in AppDelegate class(.h file) whose value gets changed from multiple ViewController classes.Also,single application-wide instance for my AppDelegate class is shared throughout my application as follows :
AppDelegate *AppD = (AppDelegate *)[[UIApplication sharedApplication] delegate];
As I could access this variable declared in AppDelegate from any ViewController class, is AppDelegate class as an example of Singleton class in this scenario?
Can anyone help to site out the usage of singleton class with real-life example ?
AppDelegate is however a singleton class but you show only use it for declaring things that applies globally in your application.For ex: If you want to change the color of navigation bar in your entire application you can use app delegate and set the color of navigation bar. Also app delegate is an object that handles different state transition in your app. So if you want to create a variable that can be changed from multiple View controllers you should create a singleton class and declare that variable in that class.
The app delegate is not supposed to be a repository for all kinds of global variables. The app delegate is supposed to be used for things that affect the whole of the application, like launch / app termination, entering the background and returning from the background, that kind of thing.
If there is state that is shared by multiple view controllers, that should exist only once, but doesn't affect the application as a whole, then you could consider creating a singleton for that state. Then again, global state that is just an artefact of how you write your code should be avoided.
AppDelegate can be used just like singleton, but I don't recommend it. It's like you can put all your classes declarations and definitions in a class.h and a class.m file. Simply import the class.h file can invoke all classes. But it will be very inconvenient to read, understand and manage.
AppDelegate is mainly used for all kinds of app itself event, through UIApplicationDelegate method. Do not recommend deal with too much logic about global data here. Such as classes named XXManager, XXService, PublicData, is proposed to manage all kinds of singleton data.
I need to pass the data from the UIViewController to AppDelegate. But should not with NSUserDefaults. I could not find an alternative for it even i don't want it.
if you need access to your delegate, I always create my singleton access in the next way :
// your imports...
#define appDelegate ((YourAppDelegate*)[UIApplication sharedApplication].delegate)
#class YourAppDelegate
//...
in the .h file of YourAppDelegate.
It´s helpful and useful, but do not abuse of the delegate to access to particular things, try to use a different way. Anyway, you can access in that way.
I'm currently working on a product that needs to swizzle the AppDelegate's application:didReceiveRemoteNotification: (I don't want to call my new method in the appDelegate itself).
The thing is: the swizzling simply doesn't work. I've already swizzled methods several times before with success, and this time, the replacing implementation is simply not called. I was wondering if that was because of some specificity of the appDelegate's methods, since these are called by the system, and not the application.
I am going to assume most of the things which are missing in your question, Its a great idea to always post the questions with code samples whenever possible.
You need to make sure that you are swizzling your methods on the specific implementation of UIApplicationDelegate and NOT the UIApplicationDelegate itself. For Eg
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#end
In this case you need to swizzle the AppDelegate Class.
Also, if you are trying to write a static library/framework you may not know the name of this class at all. In such cases simplest/safest way is to ask for App's AppDelegate name and use that to retrieve a specific class instance via NSClassFromString() or you can just brute force to find the class you need (because you usually have a Single AppDelegate class).
unsigned int numberOfClasses = 0;
Class *classes = objc_copyClassList(&numberOfClasses);
Class appDelegateClass = nil;
for (unsigned int i = 0; i < numberOfClasses; ++i) {
if (class_conformsToProtocol(classes[i], #protocol(UIApplicationDelegate))) {
appDelegateClass = classes[i];
}
}
EDIT
There are a few shortcomings of the above approach,
It iterates over all the classes accessible by your application code, looping through all of these classes is not a performant solution.
Many famous SDKs will swizzle or dynamically extend your AppDelegate, because a lot of interesting things can be intercepted at this single place. This also means that your application may have more than one implementation of UIApplicationDelegate protocol and the code above may just pick any implementation of it, causing some serious issues.
As, Chris suggested in comments below, its much safer and performant to just use [[UIApplication sharedApplication].delegate class]. which should give you the exact AppDelegate implementation known to iOS application at the moment of calling this line of code.