Inserting an NSSplitViewItem based on saved User Defaults using Swift

In a previous article, we discussed Inserting and Replacing Split View Items with Swift and Storyboards. One refinement that we can add to this example is to save which Split View Item to display as a User Default so that when the application loads, it remembers which Split View Item was last displayed between restarts of the application.

In this article we will take a look at:

NSUserDefaults Using the splitViewItems array of the NSSplitViewController to replace one NSSplitViewItem with another

Step 1:

Set up a project according to the instructions from Step 1 of Inserting and Replacing Split View Items with Swift and Storyboards.

Set the Storyboard Id of the two right View Controllers to com.example.right.1 and com.example.right.2, respectively.

Step 2:

Go to BPLeftViewController.swift and update it so that the class looks like this:

import Cocoa

class BPLeftNavViewController: NSViewController {

  var defaults = NSUserDefaults.standardUserDefaults()
  var split_id = ""
  var svc : NSSplitViewController?
  var splitViewItems = [String : NSSplitViewItem]()

  override func viewDidLoad() {
    super.viewDidLoad()
    // Call prepareSplitViewItems to set up a dictionary of NSSplitViewItems
    prepareSplitViewItems()

  }

  func prepareSplitViewItems() {
    // get the Split View Controller
    svc = self.parentViewController as? NSSplitViewController
    // get the array of Split View Items
    let vi = svc!.splitViewItems

    // add the first right Split View Item to the dictionary
    splitViewItems["com.example.right.1"] = vi[1]

    // get the second right Split View Item from the Storyboard
    let right2 = self.storyboard?.instantiateControllerWithIdentifier("com.example.right.2") as! NSViewController
    // add the second right Split View Item to the dictionary
    splitViewItems["com.example.right.2"] = NSSplitViewItem(viewController: right2)

    // wait 0.1 seconds to call displaySavedSplitViewItem so that the Scene can finish laying out
    // if you don't do this, you will get an array out of bounds exception
    let _ = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: "displaySavedSplitViewItem", userInfo: nil, repeats: false)
  }

  func displaySavedSplitViewItem() {
    // Check to see if there is a value "split_to_display" in the User Defaults for this application
    if let _split_id = defaults.valueForKey("split_to_display") as? String {
        // if found, use it
        split_id = _split_id
        svc?.splitViewItems[1] = splitViewItems[split_id]!
    } else {
        // otherwise go with the first right Split View Item and add it to the User Defaults
        split_id = "com.example.right.1"
        defaults.setValue("com.example.right.1", forKey: "split_to_display")
    }
  }

  @IBAction func changeSplit(sender : AnyObject) {
    // Check the value of split_id...
    switch split_id {
    case "com.example.right.1":
        split_id = "com.example.right.2"
    case "com.example.right.2":
        split_id = "com.example.right.1"
    default:
        break
    }
    // ...update the User Defaults...
    defaults.setValue(split_id, forKey: "split_to_display")
    // ... and display the correct right Split View Item
    svc?.splitViewItems[1] = splitViewItems[split_id]!
  }
}

Conclusion

You can use NSUserDefaults.standardUserDefaults to access and save values that your application can access whenever the application is run. The values saved must be of type NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary. If you need to save a different value type, you should convert it to NSData using NSKeyedArchiver (when saving the value) and NSKeyedUnarchiver (when retrieving the value).

You should also note that in this example we used an NSTimer to give the application time to load and finish laying out the Window and it's contents. If you don't do this, you will most likely get an "array out of bounds" exception.