swiftmacosnstextviewnsviewcontrollernsnotification

NSTextView/NSViewController\NSNotification used for a console log function for a MacOS app in SWIFT


I am trying to develop an app that includes a StoryBoard with a single NSTextView that I want to use to log app events and user feedback. I load the view from a check box switch from the Main view (the console log view is turned on by default when the app launches). All works well to this point with the app launching and the second view working with the text as expected from the viewDidLoad() function.

The NSViewController class for the console log view is:

class ConsoleLogViewController: NSViewController,NSTextViewDelegate {

@IBOutlet var textViewConsoleLog: NSTextView!

override func viewWillDisappear() {
    (representedObject as! NSButton).isEnabled = true
    (representedObject as! NSButton).state = NSOffState
}

override func viewDidLoad() {
    super.viewDidLoad()
    // Do view setup here.

    textViewConsoleLog.delegate = self
    textViewConsoleLog.string = "All new journal events are logged to this window"
    textViewConsoleLog.string?.append("\n")
    textViewConsoleLog.string?.append("*********************************************")
    textViewConsoleLog.string?.append("\n")
    textViewConsoleLog.string?.append("\n")
} // END OF viewDidLoad()


func addLogToConsoleWindow(newLogEntry: String) {
    textViewConsoleLog.string? = (newLogEntry)
    textViewConsoleLog.string?.append("\n")
} // END OF addLogToConsoleWindow()

} // END OF ConsoleLogViewController

The View is launched from the main view controller (named 'Control' in the image below) using the following function call:

    func showConsoleLogView() {
    let buttonCall = chkBoxConsoleLog
    performSegue(withIdentifier: "sequeConsoleView", sender: buttonCall)
}  //  END OF showConsoleLogView()

In the main view controller I create an instance of ConsoleLogViewController and then try and use the function addLogToConsoleWindow to append additional log information to the textViewConsoleLog. The relevant snippets of code in the Main view controller are:

    let consoleOutputPrintStatement = ConsoleLogViewController()  // Create an instance of ConsoleLogViewController to allow logging to the new console window

and

consoleOutputPrintStatement.addLogToConsoleWindow(newLogEntry: "Loading journal files")

However, when i run the app and try to write additional lines of text I get a fatal error: unexpectedly found nil while unwrapping an Optional value. It is clear that the textViewConsoleLog object is nil and is creating the error.

I assume that the problem is that I am creating another instance of the class and hence I am trying to append additional text to a textViewConsoleLog object that hasn't been initiated (or something like that!).

The app looks like this when it first runs:

enter image description here

So - am I just heading down the wrong road trying to develop a logging function to a console using NSTextView? Or am I close but need a different approach with the NSTextView to make it work? How do I update the text in the NSTextField with relevant strings generated from the other viewControllers?

Any assistance or guidance highly appreciated.


Solution

  • OK - I finally sorted out a solution using NSNotification - which in hindsight was pretty obvious.

    The relevant code in the console log view is shown below with the class being set up to receive a notification from the NotificationCenter using the addObserver method.:

    class ConsoleLogViewController: NSViewController,NSTextViewDelegate {
    
    @IBOutlet var textViewConsoleLog: NSTextView!
    
    let controlCentreDidSendConsoleNotification = "TCCEDJ.controlCentreDidSendConsoleNotification"
    let controlCentreDidSendConsoleNotificationMessageKey = "TCCEDJ.controlCentreDidSendConsoleNotificationMessageKey"
    
    
    override func viewWillDisappear() {
        (representedObject as! NSButton).isEnabled = true
        (representedObject as! NSButton).state = NSOffState
    }
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
    
        textViewConsoleLog.delegate = self
        textViewConsoleLog.string = "All new journal events are logged to this window"
        textViewConsoleLog.string?.append("\n")
        textViewConsoleLog.string?.append("*********************************************")
        textViewConsoleLog.string?.append("\n")
        textViewConsoleLog.string?.append("\n")
    
        //  set up notification centre
        let notificationCentre = NotificationCenter.default
        notificationCentre.addObserver(self, selector: #selector(addLogToConsoleWindow), name: NSNotification.Name(rawValue: controlCentreDidSendConsoleNotification), object: nil)
    } // END OF viewDidLoad()
    
    
    @objc func addLogToConsoleWindow(newLogEntry: NSNotification) {
    
        let userInfo = newLogEntry.userInfo as! [String: String]
        let newLogEntryString = userInfo[controlCentreDidSendConsoleNotificationMessageKey]!
    
        textViewConsoleLog.string?.append(newLogEntryString)
        textViewConsoleLog.string?.append("\n")
        textViewConsoleLog.scrollRangeToVisible(NSMakeRange((textViewConsoleLog.string?.characters.count)! - 1, 0))
    } // END OF addLogToConsoleWindow()
    

    } // END OF ConsoleLogViewController

    The relevant code in the main view controller is:

        // Set up variables to use with notification centre
    let controlCentreDidSendConsoleNotification = "TCCEDJ.controlCentreDidSendConsoleNotification"
    let controlCentreDidSendConsoleNotificationMessageKey = "TCCEDJ.controlCentreDidSendConsoleNotificationMessageKey"
    

    and

        let testLog = [controlCentreDidSendConsoleNotificationMessageKey: "Test log on button push"]
        let notificationCentre = NotificationCenter.default
        notificationCentre.post(name: NSNotification.Name(rawValue: controlCentreDidSendConsoleNotification), object: self, userInfo: testLog)
    

    All now works with the behaviour that I was looking for.