In the existing app, the data model is this declaration in the ImageCollectionListController:
var imageCollections = [ImageCollection]() {
didSet {
guard isViewLoaded else { return }
reloadOutlineAndSelectFirstItemIfNecessary()
}
}
Actually, the array is the data model; the "didSet" is part of the controller logic. Although there is an initial value here, what actually happens is that the window controller sets this property to an array consisting of a single collection (Desktop Pictures).
So, I would do the following:
1. Create a custom object as the root of the data model hierarchy, and make "imageCollections" an array property of the root object. This is for convenience, because once you start persisting the data model, you'll likely find other attributes to save that don't fit within the array. The root object allows you to add them later without upsetting everything downstream.
2. When you set up your document, this custom object will be what you archive and unarchive for save and open. (For a new document, you can create the 1-element array that the window controller does now, using the setup code you pull out of the window controller.)
3. In your windowDidLoad override in the window controller, retrieve the value of "self.document.model" (assuming "model" is the custom document property that holds a reference to the root model object), and set this as the "model" property of the corresponding ImageCollectionListController. The ImageCollectionListController needs to be changed to treat a nil value as if it were a data model with an empty array.
Note there's a subtlety/danger here. If you keep the array as a property of the view controller, it gets messy because it's a value object. It's different, therefore, from the array that's in the data model. You're better off giving the view controller a reference that it can use to find the model array. (Alternatively, you can use an explict NSArray, which is a reference type, or you can create a custom reference type — i.e. class — that has array-like behavior. But you may as well leverage the data model reference, and avoid extra work.)
That's most of what you need to do to adapt the app to use a data model from a document, instead of a single global data model as it does now.
>> Who should show the Open File Panel?
>> How should data be added to and removed from NSDocument when a button is clicked in a view?
The most likely choice is ImageCollectionListController, since the changes affect the UI at its level. As I said earlier, the view controller updates the data model, not the document.
There is one wrinkle, though. The document does need to be informed when the data model changes, so that it knows the data is "dirty" and must be saved. This is one reason the view controller might need to refer to the document, except that it doesn't usually work out that in practice. Instead, when a change is made that affects the data model, the changed should be encapsulated in an undo action. In a document-based application, the window's undo manager is integrated with the document's undo manager, which means the undo action gets registered with document, which in turn makes the document dirty (and not-dirty again if the change is undone, automatically). If you don't implement undo, then you'll have to have the view controller dirty the document explicitly (or you can have the window controller observe changes somehow, and have it do the dirty work).