iPhone: Which controller should handle the CRUD logic? - iphone

I'm building an app the works similar to the iPhone Notes app.
My app consist of two screens, first screen is a UITableView listing all the records. The second screen appears when you either click on one of the records or click an add button. This second view contains a UITextView where the user can add/edit the text for that record.
Both screens have a View Controller. The MyListViewController loads the records into the UITableView. When the user clicks on a record I create an instance of the MyEditViewController and push it using the pushViewController method of the Navigation Controller.
MyListViewController -> MyEditViewController
My question is which controller should handle the CRUD logic, should it be the parent controller (i.e. MyListViewController) or the edit controller (i.e. MyEditViewController)?
One thing to note is that you should be able to delete a record from MyListController by swiping a table cell and selecting delete.
You can should also be able to delete from the MyEditViewController by clicking on a delete icon.
I'm basically trying to duplicate the Notes app but am unsure what is best practice in terms of where the CRUD logic should go.

In the scenario that you describe, the best pattern to use, would be the delegate pattern.
Simply make a delegate for your MyEditViewController and make it's delegate your MyListViewController.
You define a delegate as a protocol. So in your MyEditViewController.h put in this:
#class MyEditViewController;
#protocol MyEditViewControllerDelegate <NSObject>
#required
- (void)myEditViewController:(MyEditViewController *)controller didSaveNote:(BOOL)save;
#end
and add this to your already existing MyEditViewController.h code.
#interface MyEditViewController : UIViewController
....
#property (nonatomic, retain) id <MyEditViewControllerDelegate> delegate;
#end
In your MyEditViewController.m code when you push weather the save or the cancel button you send the following message:
[self.delegate myEditViewController:self didSaveNote:YES]
or
[self.delegate myEditViewController:self didSaveNote:YES]
depending on you pushed save or cancel.
In your MyListViewController.h you adopt your newly created protocol like this:
#interface MyListViewController : UIViewController <MyEditViewControllerDelegate>
and in your MyListViewController.m you remember two things. First to implement the required delegate method:
#implementation MyListViewController
...
- (void)myEditViewController:(MyEditViewController *)controller didSaveNote:(BOOL)save
{
// Do business logic here depending on the value of save
}
and the last thing is setting your MyListViewController to the delegate of your MyEditViewController like this:
MyEditViewController *myEditViewController = [[MyEditViewController alloc] initWithNibNamed:#"MyEditViewController" bundle:nil];
[myEditViewController setDelegate:self];
That way you handle all CRUD logic in your MyListViewController and that way you can update the Table View accordingly.

I have recently developed an application that has very similar requirements. I think you should be very clear about your Model, Views & Controllers.
Model is the non-UI part of your application, the management of Notes in your case. I created a singleton object, say NotesManager, whose shared instance can be accessed from anywhere in my code. Something like [NotesManager sharedInstance]. In my application, the view controller does not read/enumerate the documents directory's contents (because thats not its job), the NotesManager does. The List view controller asks the notes manager for the notes to display. [[NotesManager sharedInstance] notesFromDocsDir];
Views are the UI part of your application. In this case, it would be the table view & the note's edit view.
Controllers are the ones that act as the link between your Views & Model. As you know, there is the ListViewController & the EditViewController.
Now, there are two types of interactions:
The first one originates from the UI & has to update the Model. For example, the user taps delete or save. In my application, i do something like [[NotesManager sharedInstance] deleteNote:Note]. You can do this from both the View controllers.
The second one originates from the Model end & updates your UI. For example, in my application, I have enabled iTunesSharing & hence a user can add/delete a note via iTunes. When such an event occurs, my UI has to update itself to reflect the current state of the documents directory. To accomplish this, the NotesManager dispatches an NSNotification. The Controller(s) registers for these notifications & updates the view.
Now for your original question, the CRUD methods reside in the NotesManager. They can be called by the Controllers or by NotesManager itself, when it detects something has changed.
HTH,
Akshay

Both. And neither.
You should use a model to store/provide the data.
ViewController's should control the views and pass instruction to the model to save changes etc.
I would do the business logic in the model - simply call the methods in the model from the viewcontrollers.
Seeing as your child viewcontroller is doing the editing it should be that which instructs the model in that instance.
The parent viewcontroller should instruct the model when you handle the deletion of the data.

I'd say that a good solution is to implement all these operations in your Model. Say, you have a class called Note that can handle CRUD operations. You will also need something like NoteCollection that will provide valid data for your table view.
MyEditViewController will always deal with a single note that should handle operations like save and delete. These should update its state in your note collection.

Related

Separating delegates from ViewController to own classes

I'm pretty new to iOs development and I've become a bit confused how should I separate my code to avoid a huge ViewController.m-file. Currently my main viewcontroller has quite a many delegates:
#interface ViewController : UIViewController <MKMapViewDelegate, HttpClientDelegate, CLLocationManagerDelegate, NSFetchedResultsControllerDelegate>
I would like to reduce the viewController code and I guess I should create separate classes to handle delegate tasks. The problem is that if I for example create singleton class for CLLocationManager and let it implement delegate methods, how do I then communicate with the view controller?
Let's say that my CLLocationManager receives a location update, how do I tell the viewController to make changes to the UI? Should I use NSNotificationCenter to post a notification and then add observer to the view controller? Or is the best way just to let viewController implement all delegate methods as it is now?
Move some of that functionality into your data model.
It's hard to say how you should manage this given the limited information you've provided, but one has to wonder whether a single view controller should really be managing a map view and keeping track of location and managing a HTTP connection and managing a Core Data fetch. Why not move some of that into your model, where it'll be somewhat easier to divide into modules?
Let's say that my CLLocationManager receives a location update, how do
I tell the viewController to make changes to the UI? Should I use
NSNotificationCenter to post a notification and then add observer to
the view controller?
A notification would be a good solution -- it provides a way for the part of your program that manages location (again, this probably belongs in the model) to communicate the change without having to know anything in particular about the parts of the program that care about changes to location. It also allows one-to-many communication -- if you have another view controller that also cares about location, it can observe the same notification.
Or is the best way just to let viewController implement all delegate methods as it is now?
I try to think about dividing responsibilities appropriately more than limiting the size of the code. If you have a class that does one job but needs a lot of code to do it, that's fine. If you have one class that manages many unrelated things, that's not so good. The trouble is that a lot of jobs seem to fall into the traditional "manages a screenful of content" role of a view controller. Try to separate the task of managing the presentation of the data (which is the view controller's rightful job) from managing the data itself (which is the model's job).
Implement a class responsible for delegate methods:
#interface DelegateManager : NSObject <MKMapViewDelegate, HttpClientDelegate, CLLocationManagerDelegate, NSFetchedResultsControllerDelegate>
-(id)initWithViewController:(ViewController*)vc;
#property (weak) ViewController *delegate;
#end
In your ViewController:
#interface ViewController : UIViewController
-(void)doSomething;
#end
In your ViewController, create an instance of DelegateManager with self as parameter. Set all your delegates' target to your DelegateManager. In the delegate methods of your DelegateManager, call [self.delegate doSomething]; to communicate back to your ViewController.

How to set the delegate to another ViewController?

I've recently started developing for the iPhone and so far I'm doing pretty good but there's this basic pattern I really don't seem to get.
Say, I have a TabBar with two views and a custom delegate protocol, thus my structure is the following:
AppDelegate.h/.m
myDelegateProtocol.h
FirstViewController.h/.m
SecondViewController.h/.m
MainView.xib
FirstView.xib
SecondView.xib
Now I want to achieve the following: I placed a button in the FirstView.xib and I'd like the IBAction which it invokes (inside FirstViewController ofc.) to send a message to the SecondViewController ([self.delegate tellSecondViewContrToSayHi]) and invoke another method which simply prints a log into the console saying "hi I'm here."
So far I know what I need to do in theory:
Specify the protocol.
Implement the protocol in the SecondViewController.
Create an id< myDelegateProtocol > delegate inside my FirstViewController,...AND last but not least:
Set the self.delegate = secondViewControllerObject.
Now, nr.4 is where the problem's at. How on earth do I link the delegate to the other viewController? I mean I'm not the one instantiating the views as the tabBar kinda does that for me,... any advise? Or am I just way too tired to notice a really stupid thing I did somewhere?
Theoretically the same question also applies to the target:action: thing,... I mean, how do I define the target?
Thanks a lot,
wasabi
You have the right idea, assuming that you want relatively tight coupling between these controllers via that delegate protocol.
Since neither controller knows about the other until that delegate property is set you need to have some object which has a reference to both of them wire up that relationship. In your case that's probably the application delegate which can create both controllers, set one as the delegate of the other, and pass both along to your tab bar controller.
What you might actually want is to have the app delegate give both controllers a reference to some shared model object. Your FirstViewController can update that model when you tap a button and your SecondViewController can observe changes to the model to update it's display (or just update its view when it appears based on the current model state). That way your controllers don't need to know anything about each other.

iPhone: How to Pass Data Between Several Viewcontrollers in a Tabbar App

I have following problem:
I have built a tabbar application with 4 tabs. I want to pass a object/variable from the first tab controller to the third one and initialize this controller with the corresponding object.
I've already done some research. The best way, corresponding to a clean model approach, would be to call some initWithObject: method on the called viewcontroller.
How can I achieve this? How can I call the init method of the receivercontroller within the callercontroller? Can you give me some code example?
Edit:
To pass data between several views/classes etc simply create some Kind of data class which holds the data beeing shared between several classes. For more information follow the link:
Singleton
You need a data model object that stores the data for application.
A data model is a customized, standalone object accessible from anywhere in the application. The data model object knows nothing about any views or view controllers. It just stores data and the logical relationships between that data.
When different parts of the app need to write or read data, they write and read to the data model. In your case, view1 would save its data to the data model when it unloads and then view2 would read that data from the data model when it loads (or vice versa.)
In a properly designed app, no two view controllers should have access to the internal data of another controller. (The only reason a view controllers needs to know of the existence of another controller is if it has to trigger the loading of that other controller.)
The quick and dirty way to create a data model is to add attributes to the app delegate and then call the app delegate from the view controllers using:
YourAppDelegateClass *appDelegate = [[UIApplication sharedApplication] delegate];
myLocalProperty = appDelegate.someDataModelProperty;
This will work for small project but as your data grows complex, you should create a dedicated class for your data model.
Edit:
To clarify for your specific case, you would add the call to the data model when the receiver viewController becomes active.
Placing the data in an init method or a viewDidLoad won't work because in a UITabBar the users can switch back and forth without unloading the view or reinitializing the view controller.
The best place to retrieve changing data is in the viewWillAppear controller method. That way the data will be updated every time the user switches to that tab.
You might want to consider NSNotificationCenter (Reference); you register the one viewcontroller with the application notification center, and send a notification when a selection is made. When the notification is received, the other viewcontroller updates itself accordingly.
I don't think this is best practice (also check syntax) however I have got away with:
in the .h
otherclassref *otherclassname
#property (assign) otherclassname otherclassref;
and in the .m
#synthesize otherclassref;
then I just assign the reference from somewhere convenient e.g. the app delegate or wherever you are instantiating your viewcontrollers.
then the view controller can get a reference to the other view controller.
I add #class secondviewcontroller to the .h file for the firstviewcontroller and put put the #imports "secondviewcontroller.h" in the .m file of the first view controller. These are called forward references and prevent compiler errors resulting from having .h files referencing each other.

Managing subviews in Cocoa Touch

I'm developing an iPhone app. I need to create a Quiz application that has different Question views embedded in it (see my similar question).
Different types of Question will have different behavior, so I plan to create a controller class for each type of Question. The MultipleChoiceQuestionController would set up a question and 3-4 buttons for the user to select an answer. Similarly, the IdentifyPictureQuestionController would load an image and present a text box to the user.
However, the docs say that a UIViewController should only be used for views that take up the entire application window. How else can I create a class to manage events in my subviews?
Thanks,
Subclassing UIViewController will provide this functionality. For example, MultipleChoiceQuestionController would be a subclass of UIViewController. MultipleChoiceQuestionController would contain the question text (UILabel or UITextView) and several buttons (UIButton). You could create a custom constructor in MultipleChoiceQuestionController that would fill the view with the relevant question string and other relevant info.
When you want to add MultipleChoiceQuestionController's view to your main view's subview, simply do the following:
[myMainView addSubview:instanceOfMultipleChoiceQuestionController.view];
I have the same problem, and according to Apple's doc, here's what you should do:
Note: If you want to divide a single
screen into multiple areas and manage
each one separately, use generic
controller objects (custom objects
descending from NSObject) instead of
view controller objects to manage each
subsection of the screen. Then use a
single view controller object to
manage the generic controller objects.
The view controller coordinates the
overall screen interactions but
forwards messages as needed to the
generic controller objects it manages.
http://developer.apple.com/iphone/library/featuredarticles/ViewControllerPGforiPhoneOS/AboutViewControllers/AboutViewControllers.html#//apple_ref/doc/uid/TP40007457-CH112-SW12
You can handle the events on the view itself, or your view controller could have a delegate class that changes for different types of question. That delegate would process the different input, and react in a different way to user touches.
Here's some code with the idea.
// In QuestionViewControllerDelegateProtocol.h
#protocol QuestionViewControllerDelegateProtocol
// Define the methods you want here
- (void)touchesBegan;
- (void)touchesEnded;
- (void)questionLoaded;
#end
// In QuestionViewController.h
#interface QuestionViewController {
id<QuestionViewControllerDelegateProtocol> delegate;
}
#end
// In QuestionViewController.m
#implementation QuestionViewController
- (void)viewDidLoad:(BOOL)animated {
[delegate questionLoaded];
}
- (void)touchesBegan {
// Some processing logic.
[delegate touchesBegan];
}
#end
This is a very nice little solution, gives you all of the advantages of a view controller without breaking apples rules.
From the page:
This is a generic controller class
that can be used to handle a subarea.
It is modelled after UIViewController,
but conforms to Apple's
recommendation.
Your view controller creates the
instances and is responsible for
managing the subview controllers.
Alternatively you can further
subdivided your view hierachy and
create subview controllers inside
other subview controllers. In both
cases the controller instantiating the
object is responsible for managing the
subview controller. The responsible
controller is referred to as 'parent
controller.' Subclasses can use the
view controller when they for example
need to show a modal dialog.
https://github.com/Koolistov/Subview-Controller

Architecting a multiview application on the iPhone

I have an app with a root view controller, a primary view controller, and a secondary view controller. I would like to be able to send a message to the primary view controller from the secondary view controller. How can I get a reference to the primary so that I can send messages to it? Is there a better way to architect this?
The short answer: you can get back to your application delegate like this:
YourAppDelegate *delegate = [[UIApplication sharedApplication] delegate];
You likely already have a pointer to your root view controller in your application delegate class. And you probably have pointers to your primary and secondary view controllers in the root controller object. So, you could write code like this:
SecondaryViewController *primary = delegate.rootController.primaryController;
You can then send messages to it to your heart's content. No outlets required; just properties to each view controller.
There are many longer answers and also a discussion about why this practice might be questionable since it introduces potentially unwanted linkages between objects. In a "pure" object oriented design, you'll follow a clean design pattern with clear linkages between objects in various directions allowing you to better reuse the code.
Another option is to pass in pointers to the objects the class will need at initialization time. Implement a new initWithSomethingOrOther for your view controller classes and pass objects in as parameters. Cache these pointers you need (don't forget to retain them) for later use.
The clean way to do it is to define a protocol for a delegate for the secondary controller which lists the methods it needs the primary controller to provide:
#protocol SecondaryControllerDelegate <NSObject>
- (void)secondaryController:(SecondaryController*)secondaryController
frobFooWithBar:(Bar*)myBar;
- (BOOL)secondaryController:(SecondaryController*)secondaryController
shouldTwiddleBaz:(Baz*)currentBaz;
#end
Now add a delegate property to the SecondaryController:
#interface SecondaryController : UIViewController {
id <SecondaryControllerDelegate> delegate;
...
}
// delegates are one of the few places you don't retain an object
#property (assign) id <SecondaryControllerDelegate> delegate;
...
In SecondaryController's implementation section, synthesize the delegate property. (Do not release it in the destructor.) When SecondaryController needs to communicate with the PrimaryController, it should call the appropriate method on the delegate.
Now make your PrimaryController implement the SecondaryControllerDelegate protocol:
#interface PrimaryController : UIViewController <SecondaryControllerDelegate> {
...
Implement the delegate methods in PrimaryController.
Finally, have your PrimaryController set itself as the SecondaryController's delegate. Exactly how you do this will depend on whether you create SecondaryController in a nib or not. If you do, make the connection there; if not, make it just after you allocate and init the SecondaryController.
Why do you do this song and dance? Well, when you have to introduce another controller between the Primary and Secondary, or use the Secondary elsewhere in the app, or even use the Secondary in another app (I have one controller that gets used in three of my four apps), you don't have to change SecondaryController at all; you just change whatever class should now be its delegate. This is an incredible time saver in the long run.
If the controllers are loaded from a NIB, you could define an outlet on the secondary controller and connect it to the primary controller in interface builder.
Use NSNotificationCenter for decoupled communication between objects.