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.
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.
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.
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.
In first example am setting the variable of App Delegate from ClassA.
In Second Example am able to call the function of App Delegate from ClassB.
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
}
- 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
iOS Swift - Custom Camera Overlay
Memory Usage Keeps Rising on Older Devices Using Metal
Return in Function Without Return Value in Swift
How to Fill a Circle Color by Percentage Value
Please Specify a Platform for This Target in Your Podfile
Convenience Initialization of Uinavigationcontroller Subclass Makes Subclass Constant Init Twice
How to Get User Data from Facebook Sdk on iOS
Transition Delegate for Uitabbarcontroller Animation
How to Hide API Keys in Github for iOS (Swift) Projects
Get Latitude and Longitude Center of Google Map
Libmobilegestalt Mobilegestalt.C:890: Mgisdeviceoneoftype Is Not Supported on This Platform
How to Set Clear Background in Table View Cell Swipe Action
Why am I Getting Com.Facebook.Sdk.Login Error 308
Adding Custom Game Logic to Scene Kit (Swift)
Changing Font Size in a Label for Only iPhone 4S, Is This Possible