Swift OSX - Delegate protocol function returns nil, crashes when unwrapping textfield value

Viewed 209

I'm working on an OSX app with Swift which makes use of an NSSplitView which holds two view controllers: "TableViewController" and "EntryViewController". I'm using delegates in order to transmit a custom NSObject ("Entry") on click from TableViewController up to the SplitViewController, then back down to the EntryViewController.

My problem is this: When the Entry object is received in the EntryViewController, any attempt to assign its properties to a text field value result in an unexpectedly found nil type error, never mind that the IBOutlets are properly linked, and that it can both print the Entry.property and the textfield string value (provided it is in a different, unrelated function).

I have tried many arrangements to solve this problem, which is why the current configuration might be a bit over-complicated. A delegate relation straight from Table VC to Entry VC caused the same issues.

Is there some way that the IBOutlets are not connecting, even though the view has loaded before the delegate is called? I've read many many articles on delegation—mostly for iOS—and yet can't seem to find the root of my problems. I'll be the first to admit that my grasp of Swift is a little bit piecemeal, so I am open to the possibility that what I am trying to do is simply bad/hacky coding and that I should try something completely different.

Thanks 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, 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
    }

}
1 Answers
Related