What Is Correct Way to Notify View Controller from Appdelegate

What is correct way to notify view controller from AppDelegate?

You need to post a notification like this:

Somewhere in your Constants file:

extension Notification.Name {
public static let myNotificationKey = Notification.Name(rawValue: "myNotificationKey")
}

In AppDelegate:

let userInfo = [ "text" : "test" ] //optional
NotificationCenter.default.post(name: .myNotificationKey, object: nil, userInfo: userInfo)

In ViewController's viewDidLoad:

NotificationCenter.default.addObserver(self, selector: #selector(self.notificationReceived(_:)), name: Notification.Name.myNotificationKey, object: nil)

Callback in view controller:

func notificationReceived(_ notification: Notification) {
//getting some data from userInfo is optional
guard let text = notification.userInfo?["text"] as? String else { return }
//your code here
}

What is correct way to notify view controller from AppDelegate?

You need to post a notification like this:

Somewhere in your Constants file:

extension Notification.Name {
public static let myNotificationKey = Notification.Name(rawValue: "myNotificationKey")
}

In AppDelegate:

let userInfo = [ "text" : "test" ] //optional
NotificationCenter.default.post(name: .myNotificationKey, object: nil, userInfo: userInfo)

In ViewController's viewDidLoad:

NotificationCenter.default.addObserver(self, selector: #selector(self.notificationReceived(_:)), name: Notification.Name.myNotificationKey, object: nil)

Callback in view controller:

func notificationReceived(_ notification: Notification) {
//getting some data from userInfo is optional
guard let text = notification.userInfo?["text"] as? String else { return }
//your code here
}

Opening view controller from app delegate using swift

You have to set ViewController StoryBoardId property as below image.

Sample Image

open viewController using coding as below in swift

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

let mainStoryboardIpad : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewControlleripad : UIViewController = mainStoryboardIpad.instantiateViewControllerWithIdentifier("Circles") as UIViewController
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = initialViewControlleripad
self.window?.makeKeyAndVisible()

return true
}

For iOS 13+ (based on an article by dev2qa)

Open SceneDelegate.swift and add following

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {

// If this scene's self.window is nil then set a new UIWindow object to it.
self.window = self.window ?? UIWindow()

// Set this scene's window's background color.
self.window!.backgroundColor = UIColor.red

// Create a ViewController object and set it as the scene's window's root view controller.
self.window!.rootViewController = ViewController()

// Make this scene's window be visible.
self.window!.makeKeyAndVisible()

guard scene is UIWindowScene else { return }
}

There is an open-source navigation utility which attempts to make this easier. Example

How do a comunication from viewController to appdelegate?

I would not recommend you to use the NotificationCenter. They have some limitations.

  1. From each ViewController, you have to use the post function and in App Delegate you have to add the observer. Then only you can use the post method of NotificationCenter. Like above David have shown you.

  2. If you have to call different functions of App Delegate from different view Controllers then again you have to add different observers in App Delegate. After that you can call the different functions of App delegate.

But Using the above method which you have define in your question. You don't need to add the observer. With the help of this Instance you can call any method of App delegate. I would show you a example:-

 let appDelegate = UIApplication.shared.delegate as! AppDelegate

@UIApplicationMain
@objc class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?
var isUpdate: Bool = false

func setValuesOfAppDelegate() {
print("123")
}

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

IQKeyboardManager.shared.enable = true

if UserDefaults.standard.bool(forKey: UserDefaultKey.isAutoLogin) == false {
startViewController()
} else {
goToRootViewAfterLogin()
}

AppUserDefaults.set(KEnglish, forKey: KSetLanguage)
self.registerForPushNotifications(application)

return true
}
}

Examples how to use the App Delegate Variables and functions from the App delegate Instance. You can have a look over the code.

  1. In first example am setting the variable of App Delegate from ClassA.

  2. In Second Example am able to call the function of App Delegate from ClassB.

  3. In Third Example am able to set the value of App Delegate Class variable and able to access the function on the same time.

    class ClassA: UIViewController {

    //MARK:- View Life Cycle
    override func viewDidLoad() {
    super.viewDidLoad()
    appDelegate.isUpdate = true
    }
    }

    class ClassB: UIViewController {

    //MARK:- View Life Cycle
    override func viewDidLoad() {
    super.viewDidLoad()
    appDelegate.setValuesOfAppDelegate()
    }
    }

    class ClassC: UIViewController {

    //MARK:- View Life Cycle
    override func viewDidLoad() {
    super.viewDidLoad()
    appDelegate.isUpdate = true
    appDelegate.setValuesOfAppDelegate()
    }
    }

How to pass data from appdelegate to a viewcontroller?

OK - you've got most of the work already done...

When using segues, iOS creates your view controller and gives you access to it in prepare, where you probably have done something like this:

if let vc = segue.destination as? detailPinViewController {
vc.myVariable = "this is the data to pass"
}

In didFinishLaunchingWithOptions, you are doing the creation part, and the "presentation" part... so you can just add in the "pass the data" part:

let destinationViewController = storyboard.instantiateViewController(withIdentifier: "detailPin2") as! detailPinViewController

destinationViewController.myVariable = "this is the data to pass"

let navigationController = self.window?.rootViewController as! UINavigationController

navigationController.pushViewController(destinationViewController, animated: false)

and that should do it.

How do I open a view controller from the app delegate?

You have 2 possible ways of doing this:

1. Instantiate your tab bar controller via storyboard.

Go to your storyboard and set a storyboard ID for your tab bar controller inside the identity inspector.

Once you go that, you can instantiate your view controller like this:

let storyboard = UIStoryboard(name: storyboardName, bundle: nil)
if let tabController = storyboard.instantiateViewController(withIdentifier: "tabControllerID") as? UITabController {
tabController.selectedIndex = index
}

  1. Or, you can access scene delegate from your AppDelegate and use window -> rootViewController to get access to your tabController:
    let scene = UIApplication.shared.connectedScenes.first
if let sceneDelegate = scene?.delegate as? SceneDelegate {
if let tabController = sceneDelegate.window?.rootViewController as? UITabBarController {
tabController.selectedIndex = index
}

}

How to check in AppDelegate if a particular ViewController is currently open

By initiating messagesVC, you're creating a brand new UserMessageViewController that won't have been presented yet. The particular instance of the controller you want will already be instantiated, so you must find it using the view controller hierarchy.

The AppDelegate gives you access to the rootViewController of your application which will be the very first controller you have in your storyboard. From this controller, you can make your way through the child view controllers in search of a UserMessageViewController.

Here is an extension that will start at the rootViewController and bubble its way up until it reaches the top of the view controller hierarchy stack.

extension UIApplication {
func topViewController(_ base: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
switch (base) {
case let controller as UINavigationController:
return topViewController(controller.visibleViewController)
case let controller as UITabBarController:
return controller.selectedViewController.flatMap { topViewController($0) } ?? base
default:
return base?.presentedViewController.flatMap { topViewController($0) } ?? base
}
}
}

Create a new file called UIApplication+TopViewController.swift and paste in the above extension. Then inside AppDelegate, you will be able to get the current view controller that is being presented using UIApplication.shared.topViewController():

if let messagesVC = UIApplication.shared.topViewController() as? UserMessageViewController {
print("Messages viewcontroller is visible and open")
} else {
print("Messages viewcontroller isnt visible and not open")
}

By casting the top view controller to UserMessageViewController, we can determine whether or not the notification should be presented.

Push to ViewController from AppDelegate with stack and navigation Bar

I think you should go with NotificationCenter to do this.

When the user click on the notification you should post a NotificationCenter in the didReceive like this:

NotificationCenter.default.post(name: .onMessageRecieved, object: notificationObject)

And based on which view controller you're in you should add an observer there in the viewDidLoad() func so it understands a message has been recieved like this:

override func viewDidLoad() {
super.viewDidLoad()

NotificationCenter.default.addObserver(self, selector: #selector(MyViewController.onMessageRecieved(notification:)), name: .onMessageRecieved, object: nil)

}

And you should have a func (onMessageRecieved) in the view controller to do the navigation like this:

    func onMessageRecieved(notification: NSNotification){

// open the conversation

let vc = self.storyboard?.instantiateViewController(withIdentifier: "ChatViewController") as! ChatViewController

vc.contactName = name as String

self.navigationController?.pushViewController(vc, animated: true)

}

This way you are doing the navigation from the view controller so the navigation will be there as well.

Presenting a specific view controller from AppDelegate

I suggest you use applicationWillEnterForeground:, not applicationDidBecomeActive:, because it works better with multitasking gestures. For example, you don't want to put up the lock screen if the user double-clicks the home button to display the task bar, and then dismisses the task bar without changing apps.

Presumably your AppDelegate has a window property, and you know that your window's root view controller is a UINavigationController.

- (void)applicationWillEnterForeground:(UIApplication *)application {
PasscodeViewController *pvc = [[PasscodeViewController alloc] init];
[(UINavigationController *)self.window.rootViewController pushViewController:pvc animated:NO];
// [pvc release]; if not using ARC
}


Related Topics



Leave a reply



Submit