Access to AppDelegate methods from NSViewController - swift

I'm trying to execute a method present in AppDelegate from my NSViewController class.
I tried this line to access to the AppDelegate :
let appDelegate = UIApplication.shared.delegate as! AppDelegate;
Without success : Use of unresolved identifier 'UIApplication'.
Tried to import UIKit, doesn't works ... cause it's for IOS ?
PS : It's hard to learn how to develop in swift ... a lot of tutorials are for objective C, and the swifts can also be for IOS ...

NSViewController is clearly macOS.
In macOS you get the application delegate with
let appDelegate = NSApp.delegate as! AppDelegate
NSApp is a shortcut for NSApplication.shared

Related

Setting Realm schema version in MacOS app

Unlike iOS, the app delegate's applicationWillFinishLaunching and applicationDidFinishLaunching are called after ViewDidLoad on the initial NSViewController.
Main.c (or main.swift) seems to be retired and without changing AppDelegate too much, the only place I can think of to call schemaVersion is in the ViewDidLoad of the initial view controller, which seems ugly to me.
What is recommended way to set Realm's schemaVersion in MacOS apps written in Swift?
The init() of the MacOS AppDelegate seems to be working great.
class AppDelegate: NSObject, NSApplicationDelegate {
override init() {
super.init()
let info = Bundle.main.infoDictionary!
let version = info["RealmSchemaVersion"] as! UInt64
Realm.Configuration.defaultConfiguration.schemaVersion = version
}
...
}

Is there a main.swift which is equivalent to the #NSApplicationMain annotation?

Creating a new Cocoa project in XCode gives me an AppDelegate.swift file which looks like this:
import Cocoa
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
#IBOutlet weak var window: NSWindow!
}
The #NSApplicationMain attribute is documented here as
NSApplicationMain
Apply this attribute to a class to indicate that it is the application delegate. Using this attribute is equivalent to calling the NSApplicationMain(_:_:) function.
If you do not use this attribute, supply a main.swift file with code at the top level that calls the NSApplicationMain(_:_:) function as follows:
import AppKit
NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
The instructions in the documentation do not work: the AppDelegate class is never instantiated. In this answer, vadian suggests the following contents for main.swift, which work better than the code in the documentation:
import Cocoa
let appDelegate = AppDelegate()
NSApplication.shared().delegate = appDelegate
_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
However, this still does not provide the same behavior as #NSApplicationMain. Consider using the above main.swift with the following AppDelegate.swift:
import Cocoa
class AppDelegate: NSObject, NSApplicationDelegate {
#IBOutlet weak var window: NSWindow!
var foo: NSStatusBar! = NSStatusBar.system();
}
The above AppDelegate.swift works with an #NSApplicationMain annotation, but when using the above main.swift, it fails at runtime with the error
Assertion failed: (CGAtomicGet(&is_initialized)), function CGSConnectionByID, file Services/Connection/CGSConnection.c, line 127.
I think this is_initialized error means that #NSApplicationMain sets things up so that the AppDelegate is instantiated after some initialization by the NSApplicationMain function. This suggests the following main.swift, which moves the delegate initialization to after the NSApplicationMain call:
import Cocoa
_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
let appDelegate = AppDelegate()
NSApplication.shared().delegate = appDelegate
However, this doesn't work either, because NSApplicationMain never returns! The above main.swift is equivalent to the broken suggestion in the documentation, because the latter two lines are dead code.
I therefore think there must be some way to pass a reference to my AppDelegate class as an argument to the NSApplicationMain function, so that Cocoa can do its initialization and then instantiate my AppDelegate class itself. However, I see no way to do this.
Is there a main.swift which provides behavior which is truly equivalent to the #NSApplicationMain annotation? If so, what does that main.swift look like? If not, what is #NSApplicationMain actually doing, and how do I modify it?
The documentation assumes that there is a xib or storyboard which instantiates the AppDelegate class via an object (blue cube) in Interface Builder. In this case both
main.swift containing NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
and
#NSApplicationMain in the AppDelegate class
behave exactly the same.
If there is no xib or storyboard you are responsible to initialize the AppDelegate class, assign it to NSApplication.shared.delegate and run the app. You have also to consider the order of appearance of the objects. For example you cannot initialize objects related to AppKit before calling NSApplication.shared to launch the app.
For example with this slightly changed syntax
let app = NSApplication.shared
let appDelegate = AppDelegate()
app.delegate = appDelegate
_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
you can initialize the status bar in AppDelegate outside ofapplicationDidFinishLaunching:
let statusItem = NSStatusBar.system().statusItem(withLength: -1)
because NSApplication.shared() to launch the app is called before initializing the AppDelegate class.
Here is what I did in order to run application without #NSApplicationMain annotation and function NSApplicationMain(_, _) while using Storyboard with initial NSWindowController generated by Xcode application template (with slight modification related to Main Menu described below).
File: AppConfig.swift (Swift 4)
struct AppConfig {
static var applicationClass: NSApplication.Type {
guard let principalClassName = Bundle.main.infoDictionary?["NSPrincipalClass"] as? String else {
fatalError("Seems like `NSPrincipalClass` is missed in `Info.plist` file.")
}
guard let principalClass = NSClassFromString(principalClassName) as? NSApplication.Type else {
fatalError("Unable to create `NSApplication` class for `\(principalClassName)`")
}
return principalClass
}
static var mainStoryboard: NSStoryboard {
guard let mainStoryboardName = Bundle.main.infoDictionary?["NSMainStoryboardFile"] as? String else {
fatalError("Seems like `NSMainStoryboardFile` is missed in `Info.plist` file.")
}
let storyboard = NSStoryboard(name: NSStoryboard.Name(mainStoryboardName), bundle: Bundle.main)
return storyboard
}
static var mainMenu: NSNib {
guard let nib = NSNib(nibNamed: NSNib.Name("MainMenu"), bundle: Bundle.main) else {
fatalError("Resource `MainMenu.xib` is not found in the bundle `\(Bundle.main.bundlePath)`")
}
return nib
}
static var mainWindowController: NSWindowController {
guard let wc = mainStoryboard.instantiateInitialController() as? NSWindowController else {
fatalError("Initial controller is not `NSWindowController` in storyboard `\(mainStoryboard)`")
}
return wc
}
}
File main.swift (Swift 4)
// Making NSApplication instance from `NSPrincipalClass` defined in `Info.plist`
let app = AppConfig.applicationClass.shared
// Configuring application as a regular (appearing in Dock and possibly having UI)
app.setActivationPolicy(.regular)
// Loading application menu from `MainMenu.xib` file.
// This will also assign property `NSApplication.mainMenu`.
AppConfig.mainMenu.instantiate(withOwner: app, topLevelObjects: nil)
// Loading initial window controller from `NSMainStoryboardFile` defined in `Info.plist`.
// Initial window accessible via property NSWindowController.window
let windowController = AppConfig.mainWindowController
windowController.window?.makeKeyAndOrderFront(nil)
app.activate(ignoringOtherApps: true)
app.run()
Note regarding MainMenu.xib file:
Xcode application template creates storyboard with Application Scene which contains Main Menu. At the moment seems there is no way programmatically load Main Menu from Application Scene. But there is Xcode file template Main Menu, which creates MainMenu.xib file, which we can load programmatically.
Replace the default Cocoa project's AppDelegate.swift with the following main.swift. The application will behave the same as before. Thus, the following code provides the semantics of the #NSApplicationMain annotation.
import Cocoa
class AppDelegate: NSObject, NSApplicationDelegate { }
let myApp: NSApplication = NSApplication.shared()
let myDelegate: AppDelegate = AppDelegate()
myApp.delegate = myDelegate
let mainBundle: Bundle = Bundle.main
let mainNibFileBaseName: String = mainBundle.infoDictionary!["NSMainNibFile"] as! String
mainBundle.loadNibNamed(mainNibFileBaseName, owner: myApp, topLevelObjects: nil)
_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
(I constructed this with much help from vadian's answer. If there are any differences in behavior between the above and the default Cocoa project application, please let me know.)

Access AppDelegate functions in tableview controller?

We are using Swift and we made a "UITableViewController" inside our project folder. We are trying to access our "AppDelegate" functions. We have tried using
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
We have also tried
let appDelegate = UIApplication.sharedApplication()
Any ideas why this is not working?
We also have
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
inside our "ViewController" and it works there.
The error is " Use of undeclared Type 'AppDelegate' "
Change that line to
let appDelegate = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext

Swift: How to access in AppDelegate variable from the View controller?

I would like to write in the text or csv (prefer) file the variable from the view controller.
Indeed I am doing a drawing app and I would like to write in the file the current position of the finger.
class ViewController: UIViewController {var lastPoint = CGPoint.zeroPoint ... }
I would like to have access to lastPoint.x and lastPoint.y from the AppDelegate. how I could do that ? Thank you.
Your question is full of confusion but if that's what you are looking for:
You can access the appDelegate by getting a reference to it like that:
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
after that if you have store a property called lastPoint in your appDelegate you can access its components very simply like that:
let x = appDelegate.lastPoint.x
let y = appDelegate.lastPoint.y
If you need to access your viewController properties from the AppDelegate, then I suggest having a reference to your view controller in your appdelegate:
var myViewController: ViewController!
then when your view controller is created you can store a reference to it in the appdelegate property:
If your create your view controller outside of your appDelegate:
Swift 1-2 syntax
var theViewController = ViewController()
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.myViewController = theViewController
Swift 3-4 syntax
var theViewController = ViewController()
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.myViewController = theViewController
If you create your view controller inside of your appDelegate:
self.myViewController = ViewController()
After that you can access your data from your viewcontroller from your appdelegate just by accessing its property like that:
let x = self.myViewController.lastPoint.x
let y = self.myViewController.lastPoint.y
Swift 3 Update
var theViewController = ViewController()
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.myViewController = theViewController
You can create a BaseViewController and write this
class BaseViewController {
lazy var appDelegate : AppDelegate {
return UIApplication.shared.delegate as? AppDelegate
}
}
and inherit other viewcontrollers with BaseViewController and access this by
class ViewController : BaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
print(self.appDelegate?.lastPoint.x)
print(self.appDelegate?.lastPoint.x)
}}
I did manage to access my ViewController from the App delegate by creating an instance of ViewController in AppDelegate.swift, like:
var mainViewController = ViewController()
func applicationWillEnterForeground(_ application: UIApplication) {
mainViewController.myVariable = "Hello".
}
Still, I don't understand how does the AppDelegate "know" that mainViewController is supposed to point to that particular ViewController. As my application has a single view and a single ViewController, that's fine, but what it I had multiple ViewControllers associated with different UIViews? Appreciate if anyone here can shed a light into that.
Best Regards,
Andre

How do I get a reference to the AppDelegate in Swift?

How do I get a reference to AppDelegate in Swift?
Ultimately, I want to use the reference to access the managed object context.
The other solution is correct in that it will get you a reference to the application's delegate, but this will not allow you to access any methods or variables added by your subclass of UIApplication, like your managed object context. To resolve this, simply downcast to "AppDelegate" or what ever your UIApplication subclass happens to be called. In Swift 3, 4 & 5, this is done as follows:
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let aVariable = appDelegate.someVariable
Swift 4.2
In Swift, easy to access in your VC's
extension UIViewController {
var appDelegate: AppDelegate {
return UIApplication.shared.delegate as! AppDelegate
}
}
Convenience Constructors
Add in AppDelegate Class at the end of code
Swift 5.7
func appDelegate() -> AppDelegate {
return UIApplication.shared.delegate as! AppDelegate
}
To use AppDelegate reference anywhere in code?
For example: Call AppDelegate Method named setRoot
appDelegate().setRoot()
This could be used for OS X
let appDelegate = NSApplication.sharedApplication().delegate as AppDelegate
var managedObjectContext = appDelegate.managedObjectContext?
It's pretty much the same as in Objective-C
let del = UIApplication.sharedApplication().delegate
Here is the Swift 5 version:
let delegate = UIApplication.shared.delegate as? AppDelegate
And to access the managed object context:
if let delegate = UIApplication.shared.delegate as? AppDelegate {
let moc = delegate.managedObjectContext
// your code here
}
or, using guard:
guard let delegate = UIApplication.shared.delegate as? AppDelegate else {
return
}
let moc = delegate.managedObjectContext
// your code here
Appart from what is told here, in my case I missed import UIKit:
import UIKit
SWIFT < 3
Create a method in AppDelegate Class for ex
func sharedInstance() -> AppDelegate{
return UIApplication.sharedApplication().delegate as! AppDelegate
}
and call it some where else for ex
let appDelegate : AppDelegate = AppDelegate().sharedInstance()
SWIFT >= 3.0
func sharedInstance() -> AppDelegate{
return UIApplication.shared.delegate as! AppDelegate
}
Try simply this:
Swift 4
// Call the method
(UIApplication.shared.delegate as? AppDelegate)?.whateverWillOccur()
where in your AppDelegate:
// MARK: - Whatever
func whateverWillOccur() {
// Your code here.
}
Here's an extension for UIApplicationDelegate that avoids hardcoding the AppDelegate class name:
extension UIApplicationDelegate {
static var shared: Self {
return UIApplication.shared.delegate! as! Self
}
}
// use like this:
let appDelegate = MyAppDelegate.shared // will be of type MyAppDelegate
Make sure you import UIKit
let appDelegate = UIApplication.sharedApplication().delegate! as! AppDelegate
it is very simple
App delegate instance
let app = UIApplication.shared.delegate as! AppDelegate
you can call a method with one line syntax
app.callingMethod()
you can access a variable with this code
app.yourVariable = "Assigning a value"
extension AppDelegate {
// MARK: - App Delegate Ref
class func delegate() -> AppDelegate {
return UIApplication.shared.delegate as! AppDelegate
}
}
In my case, I was missing import UIKit on top of my NSManagedObject subclass.
After importing it, I could remove that error as UIApplication is the part of UIKit
Hope it helps others !!!
I use this in Swift 2.3.
1.in AppDelegate class
static let sharedInstance: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
2.Call AppDelegate with
let appDelegate = AppDelegate.sharedInstance
In Swift 3.0 you can get the appdelegate reference by
let appDelegate = UIApplication.shared.delegate as! AppDelegate
As of iOS 12.2 and Swift 5.0, AppDelegate is not a recognized symbol. UIApplicationDelegate is. Any answers referring to AppDelegate are therefore no longer correct. The following answer is correct and avoids force-unwrapping, which some developers consider a code smell:
import UIKit
extension UIViewController {
var appDelegate: UIApplicationDelegate {
guard let appDelegate = UIApplication.shared.delegate else {
fatalError("Could not determine appDelegate.")
}
return appDelegate
}
}
In the Xcode 6.2, this also works
let appDelegate = UIApplication.sharedApplication().delegate! as AppDelegate
let aVariable = appDelegate.someVariable