Call app delegate method from view controller - ios

I want to know if I can call an app delegate method from another ViewController.
When the app starts, the application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool i method is called. Can I call this method a second time from another view controller?

Not sure why you want to do this. You probably shouldn't, but for the purpose of answering the question here it is:
// get a reference to the app delegate
let appDelegate: AppDelegate? = UIApplication.shared.delegate as? AppDelegate
// call didFinishLaunchWithOptions ... why?
appDelegate?.application(UIApplication.shared, didFinishLaunchingWithOptions: nil)

In Swift 3.0, you can call as:
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.anyAppDelegateInstaceMethod()

This method is called just once when app launches. You can't from ViewController. Instead make user defined method in AppDelegete. and call that method from ViewController. By getting object of AppDelegate.
AppDelegate *appDel = (AppDelegate *)[UIApplication sharedApplication].delegate;
[appDel <Your method>];

Constructors:
Add a constructor in AppDelegate Class at the end of code
Swift 3.x
class func shared() -> AppDelegate
{
return UIApplication.shared.delegate as! AppDelegate
}
Swift 2.x
func appDelegate () -> AppDelegate
{
return UIApplication.sharedApplication().delegate as! AppDelegate
}
and add a var like this
var boolForFun = Bool()
How to use reference in your class?
Method
for swift 3x
access functins or variables
AppDelegate.shared().boolForFun = true
for else
appDelegate().methodFoo()
Variable
appDelegate().foo

Swift 4, Swift 5
As others have said you shouldn't do that.
It would be better if you trigger it when you are in a certain application life cycle and do something specific using the Notification Center.
Example (in ViewController):
NotificationCenter.default.addObserver(
self,
selector: #selector(applicationWillEnterForeground),
name: UIApplication.willEnterForegroundNotification,
object: nil
)
However, if you do have to call the app delegate method, you can use this
let appDelegate: AppDelegate? = UIApplication.shared.delegate as? AppDelegate

Related

How to set variable value through AppDelegate in Swift?

I have UIViewController in Main.storyboard and set it's identifier to settings. I reference Main.storyboard and get the view controller in AppDelegate and then set the value of a variable which is located in view controller but it returns nil.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let mainStoryboardIpad : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
if let settingViewController = mainStoryboardIpad.instantiateViewController(withIdentifier: "settings") as UIViewController? {
let settingVC = settingViewController as! SettingViewController
settingVC.moc = persistendContainer.viewContext
}
return true
}
And I have following variable in SettingViewController:
var moc: NSManagedObjectContext!
I confirmed that I am able to get Main.storyboard, SettingViewController and persistentContainer.viewContext has value.
You are instantiating a new settings UIViewController successfully, but then nothing happens with it after that. Later on your app will create a Settings viewController through the storyboard, but that will be a different one.
The better way to do this is to create an accessible place for your viewController to access the moc variable rather than trying to pass it in in advance. This is usually done for you if you let Xcode create the project with CoreData set up. You can say (UIApplication.shared.delegate as! AppDelegate).managedObjectContext from any viewController in your app and get a pointer to that moc.

Firebase observer during all the app life

I'm trying to create a firebase observer that remains alive during all the app life. What I want is to change a property of my tabBarController when some data change in firebase. Here's my code:
self.ref.child("mySubRef").observe(.value , with: {snapshot in
self.tabBarController?.tabBar.items?[3].badgeValue = "!"
})
So, I've tried creating it in the viewDidLoad of my first viewController and also in the viewDidAppear. I don't remove it since I want it to be there always. In the viewDidAppear it works only if I'm in that viewController at the moment of the change. If I want that change to happen no matter where I am (always inside the tabBar) where do I have to put that code?
Thanks for the help!
I have found the answer. The problem was that when I changed between viewControllers the reference to the observer was deallocated. So, to fix it, I have created a class like this:
class NotificationListener: NSObject {
let ref:FIRDatabaseReference = FIRDatabase.database().reference()
var user:User?
func setUpListener(tabBarController:UITabBarController){
self.user = User()
self.ref.child("users/" + self.user!.uid + "/notifications").observe(.value , with: {snapshot in
tabBarController.tabBar.items?[3].badgeValue = "!"
})
}
}
Now I have a property of that class in every viewController and every one has a reference to the same object. When I change between VC it will not deallocate the object because it will still be referenced.
I think, you can use Appdelegate didFinishLaunchingWithOptions method but I'm not sure.
Like this:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
FIRApp.configure()
FIRDatabase.database().reference().child("mySubRef").observe(.value, with: { (snapshot) in
//I'm not sure for this part.
UITabBarController.init().tabBar.items?[3].badgeValue = "!"
})
return true
}

Transform UIApplicationDelegate methods into RxSwift Observables

In RxSwift / RxCocoa you can create a reactive wrapper for a delegate (e.g. UIScrollViewDelegate or CLLocationManagerDelegate) to enable Rx observable sequences for certain delegate methods.
I am trying to implement this for the UIApplicationDelegate method applicationDidBecomeActive:
What I tried so far is pretty straightforward and similar to the DelegateProxy subclasses that are included in RxCocoa.
I created my DelegateProxy subclass:
class RxUIApplicationDelegateProxy: DelegateProxy, UIApplicationDelegate, DelegateProxyType {
static func currentDelegateFor(object: AnyObject) -> AnyObject? {
let application: UIApplication = object as! UIApplication
return application.delegate
}
static func setCurrentDelegate(delegate: AnyObject?, toObject object: AnyObject) {
let application: UIApplication = object as! UIApplication
application.delegate = delegate as? UIApplicationDelegate
}
}
And an Rx extension for UIApplication:
extension UIApplication {
public var rx_delegate: DelegateProxy {
return proxyForObject(RxUIApplicationDelegateProxy.self, self)
}
public var rx_applicationDidBecomeActive: Observable<Void> {
return rx_delegate.observe("applicationDidBecomeActive:")
.map { _ in
return
}
}
}
In my AppDelegate I subscribe to the observable:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// the usual setup
// and then:
application.rx_applicationDidBecomeActive
.subscribeNext { _ in
print("Active!")
}
.addDisposableTo(disposeBag)
return true
}
When I start my app "Active!" gets printed and then I get the following crash in RxCocoa's _RXDelegateProxy_ class:
Does anybody have an idea what the problem might be? Or has anybody successfully implemented something like rx_applicationDidBecomeActive?
It looks like a really tricky issue with RxSwift and memory management.
The default implementation of DelegateProxyType sets an instance of a delegate proxy (in this case, RxUIApplicationDelegateProxy) to the delegate of UIApplication.
It also stores the original AppDelegate as a property called forwardToDelegate so all the delegate methods can still be passed to it.
The problem is that, when the new app delegate is set:
application.delegate = delegate as? UIApplicationDelegate
the original one is deallocated! You can check it by overriding deinit in AppDelegate. The reasons are explained in this answer. And because the property forwardToDelegate is of type assign, your app crashes as the property points to a deallocated object.
I have found a workaround for that. I'm not really sure if it is a recommended way, so be warned. You can override a method from DelegateProxyType in RxUIApplicationDelegateProxy:
override func setForwardToDelegate(delegate: AnyObject?, retainDelegate: Bool) {
super.setForwardToDelegate(delegate, retainDelegate: true)
}
In normal circumstances, you don't want to retain the delegate as it leads to a retain cycle. But in this special case, this is not a problem: your UIApplication object will exist the entire time while your application is alive anyway.

Swift: getting AppDelegate instance as a class func results in Thread 1: EXC_BAD_INSTRUCTION

I want to get my AppDelegate reference from a class func in my AppDelegate. Why is this throwing a Thread 1: EXC_BAD_INSTRUCTION?
class func getDelegate() -> AppDelegate {
return UIApplication.sharedApplication().delegate as! AppDelegate
}
I have also tried to move this to another utility class and as a regular func, but getting the same crash.
Is there a way I can access the AppDelegate as a class func instead of having to write
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
in every class?
Declare class function to get appDelegate in AppDelegate class as
class func getDelegate() -> AppDelegate {
return UIApplication.sharedApplication().delegate as! AppDelegate
}
To access and use appDelegate in other class, call
let delegate = AppDelegate.getDelegate()
delegate.printHello()
Create a class as shown below[in a new class file or in existing class file outside the previous class. No need to put it inside AppDelete.swift file]
class Delegate
{
static var appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
}
and you can use it as Delegate.appDelegate in any class.

className.type does not have a member called 'appDel'

I have a reference to my app delegate
let appDel: AppDelegate = (UIApplication.sharedApplication().delegate) as AppDelegate
That works just fine, but this line of code below gives a error:
let context: NSManagedObjectContext = appDel.managedObjectContext
It gives me the following error:
'vctGebruikers.Type' does not have a member named 'appDel'
I declared them right below my class like this:
class vctGebruikers: UIViewController, UITableViewDelegate, UITableViewDataSource {
let appDel: AppDelegate = (UIApplication.sharedApplication().delegate) as AppDelegate
let context: NSManagedObjectContext = appDel.managedObjectContext
}
The weird thing is when I paste the code in viewDidLoad or in a function the code just works fine. And I can't figure out what the problem is.
How can I solve this error?
EDIT:
I also need to acces context here:
let results: NSArray = context.executeFetchRequest(request, error: nil)
This is what I got working thanks to #Antonio, but now im not able to acces context and appDel
init(coder aDecoder: NSCoder!) {
let appDelegate = (UIApplication.sharedApplication().delegate) as AppDelegate
self.appDel = appDelegate
self.context = appDelegate.managedObjectContext
super.init(coder: aDecoder)
}
In swift you cannot reference self until all class properties have been initialized.
You are implicitly using self in this line:
let context: NSManagedObjectContext = appDel.managedObjectContext
The solution is to initialize them in an initializer, but using something like:
let appDelegate = (UIApplication.sharedApplication().delegate) as AppDelegate
self.appDel = appDelegate
self.context = appDelegate.managedObjectContext
Also read here, a similar question asked a couple days ago on SO.
One method of instantiating the context is to drill down instead of up from AppDelegate
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
if let vc = window?.rootViewController as? UIViewController {
vc.context = self.managedObjectContext}
`
And in your view controller, tell it to populate dynamically
var context: NSManagedObjectContext!
Also don't forget to call
import CoreData everywhere you are using said objects
One more final method: Use a closure so that your reference will happen at run time:
var context: NSManagedObjectContext {
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
return appDelegate.managedObjectContext
}

Resources