1

I would like to know is it possible to, let say, change or modify an object like a button of another view controller from AppDelegate.swift. Here is what I tried to begin with but somehow stucked.

func application(_ application: UIApplication,didReceiveRemoteNotification userInfo: [AnyHashable : Any],fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {

   if application.applicationState == .active {
        if self.window?.rootViewController is homeViewController {
            //modify a button exist in homeViewController


        }
   }

}

Any help is appreciated. Thanks in advance.

Zaki Zakaria
  • 127
  • 1
  • 17
  • Theoretically it's possible but it will break the MVC pattern (https://developer.apple.com/library/content/documentation/General/Conceptual/DevPedia-CocoaCore/MVC.html). If you cast rootViewController to homeViewController you can access his properties. – LorenzOliveto Jan 09 '18 at 11:14
  • Oh I see..The thing is I wanted to update my button in homeViewController depending on the notifications received...so, is there any alternative other than what I have ? @lorenzoliveto – Zaki Zakaria Jan 09 '18 at 11:18
  • You can use NotificationCenter as already posted in the answers. – LorenzOliveto Jan 09 '18 at 11:51

4 Answers4

2

You can use NotificationCenter! add observer in your view controller and remove it while you go back from that view controller(custom notification). and when you receive notification post it!

If you don't have idea that how to deal with NotificationCenter then refer This SO post !

Ketan Parmar
  • 27,092
  • 9
  • 50
  • 75
  • Notification Center is a broadcasting. It is appropriate when you need to notify multiple instances. But when you need to notify single object it is hardly a case. – MichaelV Jan 09 '18 at 11:35
  • @MichaelVorontsov : Nothing is wrong if you use `NotificationCenter` to notify single object! – Ketan Parmar Jan 09 '18 at 11:37
  • Apple says "A notification dispatch mechanism that enables the broadcast of information to registered observers" that doesn't mean number of observer should be greater than one! @MichaelVorontsov – Ketan Parmar Jan 09 '18 at 11:40
  • If you want to call your fried are you going to use phone or broadcast via radio station? – MichaelV Jan 09 '18 at 11:51
  • @MichaelVorontsov : Hehehe! This is different case! or I can say that irrelevant example! – Ketan Parmar Jan 09 '18 at 12:23
2

You can use the NotificationCenter to send and receive internal notifications (note they are different from local and remote notifications).

First create your notification doing something like this:

extension Notification.Name {
    static let remoteNotificationReceived = Notification.Name("uk.co.company.app.remoteNotificationReceived")
}

Then in your view controller that is to respond do something like this:

class TestViewController: UIViewController {
    var remoteNotificationReceivedObserver: NSObjectProtocol?

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        remoteNotificationReceivedObserver = NotificationCenter.default.addObserver(forName: Notification.Name.remoteNotificationReceived, object: nil, queue: nil, using: { (notification) in
            DispatchQueue.main.async { // because the notification won't be received on the main queue and you want to update the UI which must be done on the main queue.
                // Put the code to change the button here
            }
        })
    }

    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)

        if let remoteNotificationReceivedObserver = remoteNotificationReceivedObserver {
            NotificationCenter.default.removeObserver(remoteNotificationReceivedObserver)
        }
    }
}

Then elsewhere in you app you post the notification like this:

    NotificationCenter.default.post(name: Notification.Name.remoteNotificationReceived, object: nil)
Upholder Of Truth
  • 4,643
  • 2
  • 13
  • 23
1

The only place you should really be interacting with another vew controller like that is during segues (if you are using Storyboards). Even then, you should let the view functions for that controller be responsible for changing the state of its buttons and just pass some variable to the controller or perhaps better set up the controller to listen for notifications. Then your app delegate, or other controller can just post notifications that your home controller listens for.

davidethell
  • 11,708
  • 6
  • 43
  • 63
1

It is possible, but addressing directly members of another ViewController breaks responsibility. It is a good practice to define Interface protocols for internal interactions. In this particular case, it is a good idea to create protcol RemoteNotificationReciverInterface (or kind of RemoteNotificationReciveable according to some modern coding styles advice, although I found it difficult to find appropriate adjective in this case) :

protocol RemoteNotificationReciverInterface: class {
    func didReciveNotification(info : [AnyHashable : Any])
}

Then extent your ViewController( and any view controllers that had to react on Notifications when they are topmost)

extension HomeViewController: RemoteNotificationReciverInterface {
    func didReciveNotification(info : [AnyHashable : Any]) {
        // Chnage you button, ignore para,eters
    }
}

You can adopt UINavigationContoroller, UITabBarConroller etc. to forward notifications to their topmost controllers, like:

extension UINavigationController: RemoteNotificationReciverInterface {
    func didReciveNotification(info : [AnyHashable : Any]) {
        (topViewController as? RemoteNotificationReciverInterface)?.didReciveNotification(info: info)
    }
}

And the easily forward it from app delegate.

func application(_ application: UIApplication,didReceiveRemoteNotification userInfo: [AnyHashable : Any],fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
    if application.applicationState == .active {
        (window?.rootViewController as? RemoteNotificationReciverInterface).didReciveNotification(info: userInfo)
    }
}
MichaelV
  • 1,231
  • 8
  • 10