6

I need to call a method in my ViewController class in AppDelegate. This is what the method looks like this:

class ViewController: UIViewController {

    func myFunction() {
        ...
    }
}

I've tried using window.rootViewController, but it's telling me the method myFunction does not exist, since rootViewController is of type UIViewController.

I've also tried

var vc = ViewController()
self.vc.myFunction()

But this is creating a new instance of the ViewController class, and that's not what I want, either.

Here is the part of the AppDelegate code that I need myFunction to be called from:

class AppDelegate: UIResponder, UIApplicationDelegate {

    func handleEvent() {
        if UIApplication.shared.applicationState == .active {
            // call myFunction()
        }
        else {
            ...
        }
    }
}

Context: I'm following a geofencing tutorial: https://www.raywenderlich.com/136165/core-location-geofencing-tutorial

Since user location is being monitored even when app is closed, when an event is triggered, AppDelegate should be used to handle the event, since view controller is not yet loaded.

In my AppDelegate that handles event, if the application is active, I would like to pop up an alert to have the user choose to stop monitoring location. The function that stops location monitoring (myFunction) is in view controller.

Asteria
  • 107
  • 1
  • 2
  • 6
  • 1
    It's a bad design to try to call anything in your view controller from the app delegate. What are you trying to accomplish? And show the relevant app delegate code. – rmaddy Apr 22 '17 at 23:56
  • As rmaddy points out, you shouldn't be calling methods on a view controller that hasn't even been fully assembled (it is not yet displayed). However, you can set properties. So tell us why need to call a function in the view controller that doesn't even exist, yet. – Mozahler Apr 23 '17 at 18:58
  • Edited my post. – Asteria Apr 23 '17 at 21:48

3 Answers3

6

if its the root its easy:

(window.rootViewController as? ViewController)?.myFunction()

if its on top of a navigationController

((window.rootViewController as? UINavigationController)?.topViewController as? ViewController)?.myFunction()

But in either case its weird and probably not the right thing to do. Use NotificationCenter instead and post a notification name in your AppDelegate and then have the ViewController listen for messages and respond accordingly.

Josh Homann
  • 15,933
  • 3
  • 30
  • 33
  • 1
    One line Swift [NotificationCenter implementation here](https://stackoverflow.com/a/45066428/428981) - Objective-C code is in another answer on that page as well – RanLearns May 06 '18 at 23:30
1

You shouldn't call view controllers methods from the appdelegate. Is a bad practice and generally is not required. If you want to react to methods such as: willResignActive willTerminate

you should listen to notifications for those events using the NotificationCenter.

dmlebron
  • 861
  • 6
  • 16
0

Sending a Notification sounds like a cleaner way. But to do what the OP is trying although not clean I used to do it on the early Xcode days and I had no issues when done.

Let's say you want to call myMethod in myViewCtlr from AppDelegate.m

in App Delegate do this

//In AppDelegate.h define
...
UIViewController *cur_ViewCtrl
... 
@property (nonatomic, retain) UIViewController *cur_ViewCtrlr;


// in the AppDelegate.m you can call the method like this:

@synthetize cur_ViewCtrlr;
... 
if([cur_ViewCtrlr isKindOfClass:[myViewCtlr class]])
       [(myViewCtlr*) cur_ViewCtrlr myMethod:arg];
...    

Then you just set the pointer to the current controller as this, for simplicity I put on the ViewDidLoad you may have to do it in the ViewWillAppear if you are flipping thru diff screens, but the idea is the same

// In myViewController
...
- (void)viewDidLoad
{
  ...
  AppDelegate *My_delegate = (AppDelegate *)[[UIApplication sharedApplication]delegate];
  //and set the reference to this conrtoller in the appDelegate
  My_delegate.cur_ViewCtrlr = self; 
...
becker
  • 351
  • 4
  • 8