Which template should you use to edit properties with a modal view controller on iPhone?
I'm looking for a good example for editing basic properties with a modal view on the iPhone.
Suppose I am building an application that acts as a Contacts application. The "detail" view controller displays all of the contact properties in a UITableView. When the UITableView enters edit mode, the cells display an expand icon. Clicking on a cell causes the "editor" view modal controller to display a view that allows the user to modify the selected property. This view often contains only one text box or collector. The user clicks the Cancel / Save button and the Editor view is discarded and the Detail view is updated.
In this scenario, which view is responsible for updating the model?
In the editor view, you can directly update the property using Key-Value Coding. This appears in the CoreDataBooks example. This makes sense to me at some level because it treats the property as a model for the editor's view editor.
However, this is not a template suggested by the View Controller Programming Guide. This assumes that the "editor" controller must define the protocol that the "detail" controller accepts. When the user indicates that they are done with editing, the "detail" view controller is called back with the entered value and rejects the "editor" view. Using this approach, the "detail" controller updates the model. This approach seems problematic if you are using the same "editor" for multiple properties, since there is only one callback method.
I would like to get some feedback on which approach works best.
a source to share
I don't think any of Apple's examples (or anyone else) actually show you how to structure an entire real world application. Instead, each example is just a test harness that shows you how to use one specific API function, but disregards how that function actually integrates with anything else. Data models are given by a particularly short offset.
I prefer a design where the data model is the backbone of the application, which is where all the view-controller / view pairs reside. View controllers do not communicate directly with each other, but instead communicate through the data model. The data model keeps track of what information the application is currently running, and therefore what data is required by each particular view controller at any given time.
Applications such as Contact Manager can be used as an example. The main data model will be a list of contact objects, each of which, in turn, will contain contact attributes. The data model has complete control over access to data and controls what data is currently being used. The user interface is hierarchical, so that the user first sees a list of all contacts in the masterView, and then information about each contact in the contactDetailView, and then can edit each contact attribute in the custom attribute edit view for each data type, so there is nameEditView, phoneDetailView, emailEditView and etc. Each one has an associated masterVC view controller, contactDetailVC, nameEditVC, etc.
To build this tabular view, masterVC sets the data model for the number of contacts, their partitions by partitions, and then queries each specific contact object for each specific index path so that it can map the table. When the user selects a row in the table, masterVC tells the data model whose contact object was selected by sending it the index. Then masterVC clicks on Contact DetailVC. He does not do anything.
When a DetailVC contact is activated, it queries the data model for the active, active Contact. He does not know or care how the current contact was selected, or even what kind / VC came before it. The data model returns an active active contact. When the user selects a field, contactDetailVC tells the data model which contact attribute was selected, and then pushes the correct editorVC onto the stack.
When the editor loads, the VC queries the data model for the current contact and the current attribute being edited. It does not know or care how the current contact attribute was selected, or even what kind / VC preceded it. When the user makes a change, ask the data model to save the changes (the data model may fail if validation fails for any reason) and then pops up on its own.
Internally, I like to implement the data model in two parts, each driven by a separate object. One of them is an abstract data manager, in this case the Core Data stack. A second default user manager keeps track of the actual state of operations in the data model and saves them by default for users. The main object holds these objects as attributes and serves as the interface to the data model.
This type of model makes it easy to suspend, resume, or restart an application to its previous state. Since every view / VC is self-contained, when you restart your application you just pop all the views on the stack without animation and the last one pops up completely filled with data even if the user didn't select anything in the previous views and really did not even see them.
It also protects the user from data loss in the event of a crash, as each VC saves its data and application state every time it is pushed or popped up. It's easy to add an extra view / VC, because each VC needs to know and communicate with the data model, not a bunch of other VCs. This makes it easier to use application components across different versions of an application or across applications.
Edit:
Are you just copying the code if the operators to map the attributes to the correct editor, or are you doing something more dynamic based on the object / attribute metadata?
In most of the more common constructs, a Contact object can have a variable number of phone numbers, emails, or other data fields, so each attribute will actually be a separate object, and Contact will have relationships that point to those objects. When the user has selected this contact for editing in the ContactDetailView, the data model will simply mark the NSManagedObject representing the desired contact attribute. This can be done by setting the "lastChosenAttribute" attribute or by storing the URI for the object. When the editor load is loaded, it will be hardcoded to ask the data model for "lastChosenAttribute" and receive an NSManagedObject for phone #, email, etc. The editor will make changes to this object, and they will be automatically saved to the contact.
For data that is singular, such as a name or a name, the data model will provide editorVC with a contact object, and editorVC will be hardcoded to ask for a contact object for that particular attribute.
a source to share
This is a tricky challenge - the View Controller Guide seems to be more straightforward conceptually, but the other method may be easier, especially if you are using Core Data. To give a general generalized opinion, I would say using my first method if you are using Core Data, since managed objects essentially have their own context and can update themselves (and classes such as NSFetchedResultsController
can automatically respond to updates).
If you are not using Core Data, I would go with the "official" recommendation as it makes it easier to manage updated properties manually. As far as worrying about multiple properties, it is certainly possible to have multiple delegate methods and call the appropriate one. For instance:
//if property is an address
if ([self.delegate respondsToSelector:@selector(editorView:didUpdateAddress:)])
[self.delegate editorView:self didUpdateAddress:theAddress];
//if property is a name
if ([self.delegate respondsToSelector:@selector(editorView:didUpdateName:)])
[self.delegate editorView:self didUpdateName:theName];
This can get complicated though - you probably want to have an abstract superclass for properties or something.
a source to share