Swift OSX - Delegate protocol function returns nil, crash when expanding textbox value

I'm working on an OSX app with Swift that uses an NSSplitView that contains two view controllers: "TableViewController" and "EntryViewController". I am using delegates to pass a custom NSObject ("Entry") when clicking from TableViewController to SplitViewController, then back to EntryViewController.

This is my problem . When an Entry object is received in the EntryViewController, any attempt to assign its properties to the text field results in an unexpectedly encountered nil error, no matter that the IBOutlets are correctly linked and that it can both print the Entry.property value and the text field string value (provided that it is in another unrelated function).

I have tried many ways to solve this problem, so the current configuration might be a little tricky. Relationships between delegates directly from the VC Table in Entry VC caused the same problems.

Is there a way the IBOutlets don't connect even though the view has loaded before the delegate is called? I've read a lot of articles about delegation - mostly for iOS - and still can't find the root of my problems. I'll be the first to admit that my understanding of Swift is a bit piecemeal, so I'm open to the possibility that what I'm trying to do is just bad / hacky coding and that I should try something completely different.

Thank you for your help!

TableViewController:

protocol SplitViewSelectionDelegate: class {
    func sendSelection(_ entrySelection: NSObject)
}

class TableViewController: NSViewController {

    @IBOutlet weak var searchField: NSSearchField!
    @IBOutlet var tableArrayController: NSArrayController!
    @IBOutlet weak var tableView: NSTableView!

    var sendDelegate: SplitViewSelectionDelegate?

    dynamic var dataArray = [Entry]()

// load array from .plist array of dictionaries

    func getItems(){
        let home = FileManager.default.homeDirectoryForCurrentUser
        let path = "Documents/resources.plist"
        let urlUse = home.appendingPathComponent(path)

        let referenceArray = NSArray(contentsOf: urlUse)
        dataArray = [Entry]()

        for item in referenceArray! {
            let headwordValue = (item as AnyObject).value(forKey: "headword") as! String
            let defValue = (item as AnyObject).value(forKey: "definition") as! String
            let notesValue = (item as AnyObject).value(forKey: "notes") as! String

            dataArray.append(Entry(headword: headwordValue, definition: defValue, notes: notesValue))
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        self.sendDelegate = SplitViewController()
        getItems()
        print("TVC loaded")
        // Do any additional setup after loading the view.
    }   

// send selection forward to entryviewcontroller

    @IBAction func tableViewSelection(_ sender: Any) {

        let index = tableArrayController.selectionIndex
        let array = tableArrayController.arrangedObjects as! Array<Any>
        let obj: Entry
        let arraySize = array.count
        if index <= arraySize {
            obj = array[index] as! Entry
            print(index)
            print(obj)
            sendDelegate?.sendSelection(obj)
        }
        else {
            print("index unassigned")
        }
        }

}

      

SplitViewController:

protocol EntryViewSelectionDelegate: class {
    func sendSecondSelection(_ entrySelection: NSObject)
}

class SplitViewController: NSSplitViewController, SplitViewSelectionDelegate {

    var delegate: EntryViewSelectionDelegate?
    @IBOutlet weak var mySplitView: NSSplitView!

    var leftPane: NSViewController?
    var contentView: NSViewController?

    var entrySelectionObject: NSObject!

    override func viewDidLoad() {

        super.viewDidLoad()

    // assign tableview and entryview as child view controllers
        let story = self.storyboard

        leftPane = story?.instantiateController(withIdentifier: "TableViewController") as! TableViewController?
        contentView = story?.instantiateController(withIdentifier: "EntryViewController") as! EntryViewController?

        self.addChildViewController(leftPane!)
        self.addChildViewController(contentView!)

        print("SVC loaded")
}

    func sendSelection(_ entrySelection: NSObject) {
        self.delegate = EntryViewController() //if this goes in viewDidLoad, then delegate is never called/assigned
        entrySelectionObject = entrySelection
        print("SVC:", entrySelectionObject!)
        let obj = entrySelectionObject!
        delegate?.sendSecondSelection(obj)
    }

}

      

And finally the EntryViewController:

class EntryViewController: NSViewController, EntryViewSelectionDelegate {

    @IBOutlet weak var definitionField: NSTextField!
    @IBOutlet weak var notesField: NSTextField!
    @IBOutlet weak var entryField: NSTextField!

    var entryObject: Entry!

    override func viewDidLoad() {
        super.viewDidLoad()
        print("EVC loaded")
    }

    func sendSecondSelection(_ entrySelection: NSObject) {
        self.entryObject = entrySelection as! Entry
        print("EVC:", entryObject)
        print(entryObject.headword)
// The Error gets thrown here:
        entryField.stringValue = entryObject.headword
    }

}

      

+3


source to share


1 answer


You don't need a delegate / protocol as there is a reference to EntryViewController

( contentView

) - by the way, the instance created with EntryViewController()

is not an instance in viewDidLoad

.

Just use the link contentView

:



func sendSelection(_ entrySelection: NSObject) {
    contentView?.sendSecondSelection(entrySelection)
}

      

+1


source







All Articles