Home > other >  How to use NSAlert Sheet Modal from a Utility class
How to use NSAlert Sheet Modal from a Utility class

Time:01-19

I am trying to write a method in a UtilityController Class for NSAlerts, but the method is not displaying the alert. I am calling it from the viewDidAppear() method in my ViewController, so I assumed this would work since the view would already be on the screen. Right?

The reason I am calling this from the viewDidAppear() method is because in my actual application I am initially loading data from CoreData, which is called from the viewDidAppear() method at program start and comparing that data with a data file on disk. If an inconsistency is detected while loading during comparison, I want to give the user an option to continue loading the changed data, or exit the app and fix what needs to be fixed.

import Cocoa

class ViewController: NSViewController {
    
    let utility = UtilityController()
    let dataController = DataController() // <-- EDIT

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
    }
    
    override func viewDidAppear() {
        super.viewDidAppear() // <-- EDIT
        dataController.fetchData() // <-- EDIT
    }

    override var representedObject: Any? {
        didSet {
        // Update the view, if already loaded.
        }
    }

}
import Cocoa

class UtilityController: NSObject {

    func showAlert() {
        let alert = NSAlert()
        // Create an alert notification based upon what was discovered.
        alert.messageText = "Warning: This is an alert!"
        alert.informativeText = "Something informative will be here!"
        alert.addButton(withTitle: "Continue")
        alert.addButton(withTitle: "Exit")
        if let mainViewWindow = NSApplication.shared.keyWindow?.contentViewController?.view.window {
            alert.beginSheetModal(for: mainViewWindow) { (returnCode: NSApplication.ModalResponse) -> Void in
                // Continue = 1000
                // Exit = 1001
                alert.window.close()
                switch returnCode.rawValue {
                case 1001: // Exit
                    exit(0)
                default: // Continue
                    return
                }
            }
        }
    }
}

EDIT: I added this class, which I left out of the original post

import Cocoa

class DataController: NSObject {
// This is the controller class that performs CoreData CRUD

    let utility = UtilityController()

    func fetchData() {
        // do the CoreData fetch of the data and if an error is found
        utility.showAlert()
    }
}

CodePudding user response:

It would be much easier to extend NSViewController and make showAlert an instance method of it. This way you would have access to the view controller's view.window property of the view controller which is calling this method. Note don't forget to call super when overriding viewDidAppear method. Note that your approach should work if you build and run your app (this is just an issue of running your app from Xcode) but be aware that your method might fail if your app is not active when executing that code. Using the view controller's view.window property is much safer:

extension NSViewController {
    func showAlert() {
        let alert = NSAlert()
        alert.messageText = "Warning: This is an alert!"
        alert.informativeText = "Something informative will be here!"
        alert.addButton(withTitle: "Continue")
        alert.addButton(withTitle: "Exit")
        if let window = view.window {
            alert.beginSheetModal(for: window) { modalResponse in
                alert.window.close()
                switch modalResponse.rawValue {
                case 1001: // Exit
                    exit(0)
                default: // Continue
                    return
                }
            }
        }
    }
}

Usage:

class ViewController: NSViewController {
    override func viewDidAppear() {
        super.viewDidAppear()
        showAlert()
    }
}

edit/update:

If you don't want to extend NSViewController the safest approach is to pass the window when calling your method:

class ViewController: NSViewController {  
    let utility = UtilityController()
    let dataController = DataController() 
    override func viewDidAppear() {
        super.viewDidAppear()
        dataController.fetchData(view.window) 
    }
}

class UtilityController: NSObject {
    func showAlert(_ sender: NSWindow?) {
        guard let sender = sender else { return }
        let alert = NSAlert()
        alert.messageText = "Warning: This is an alert!"
        alert.informativeText = "Something informative will be here!"
        alert.addButton(withTitle: "Continue")
        alert.addButton(withTitle: "Exit")
        alert.beginSheetModal(for: sender) { modalResponse in
            alert.window.close()
            switch modalResponse.rawValue {
            case 1001: 
                exit(0)
            default: 
                return
            }
        }
    }
}

import Cocoa

class DataController: NSObject {
    let utility = UtilityController()
    func fetchData(_ sender: NSWindow?) {
        utility.showAlert(sender)
    }
}
  •  Tags:  
  • Related