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

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.

Related

Saving data from ViewControllres (Core Data)

I looked through a core data tutorial which puts the core data code in the AppDelegate class that is created upon creating a Window project. I was able to successfully save and retrieve data from the premade AppDelegate class.
My question is, if i have a ViewController that saves data, how do i go about saving data from that specific class.
Do i need to redefine the persistent store and managedObjectModel in that class?
If that is the case, what is the programmatic way to do it (since previously it was generated for me) i.e what methods/instancevariables/properties do i need to declare.
Standing by for any clarifications there may be
No need to go through the app delegate or redefine persistent store etc. All you need is a reference to the managedObjectContext. Normally, this is a property (say, of a view controller) that you can set from the outside after creation and easily access from inside the view controller class (much easier than going through the app delegate!). You can pass this on easily to other view controllers, and read from and write to core data with the standard methods.
If you want to save your own objects, yes - you have to create your own managed object model (by modifying the one the template generated for you).
Why not just have a reference to your application delegate in the view controller you wish to save data in? You can then access the managed object context and call its save method as usual.
In the .h file of your view controller have a property of same type as you application delegate. Then in the viewDidLoad method in the .m file set the reference like this:
self.appDelegate = (MyApplicationDelegate*)[[UIApplication sharedApplication] delegate];
Then when you want to save you can access the application delegates managed object context like so:
[self.appDelegate.managedObjectContext save:&error];
Hope that helps, I wrote that off the top of my head so there might be some syntax errors but the compiler will let you know.

iPhone: Which controller should handle the CRUD logic?

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.

iPhone MVC. Problems with the model

I'm new to programming, iphone application programming in specific. After reading a bunch about MVC I decided to give it a try in a small application. As to my understanding, MVC works like this:
Model: data, manipulating data, retrieving data.
ViewController: formats data from the model (NSDate to specific style), etc.
View: the actual gui.
If this is indeed a correct formulation of basic MVC theory, my confusion lies in how data is passed between the model, VC, and view. Example: if I make calls to twitter and get the data in the model, how do I (correctly) pass this information to the VC for further work. I know that between the VC and View one mostly uses IBOutlets. The Model is my real problem.
In my last app I made an NSString variable in the app delegate so I could access that data from any class. However, I read that this is not the best approach when the app becomes complex because the delegate is in charge of starting, ending the app, not holding data.
I've read of delegation methods, singleton's, NSNotification (which I've used to call methods in other classes). The problem is that I don't really understand how to use these techniques to pass data from the model to other views.
Please let me know if my question is unclear.
If you think about reusability, the main components that can be reused again are your model objects and view objects. They can be moved to different apps and still used properly. Your view controller is what is really specific to your app and where most of the app logic lies.
So in your example, you could have a twitter object that stores information and tweets from a user perhaps. You would create that class with all its functions separately within its own .h and .m file. Then in your view controller, instantiate the twitter class with data that is retrieved and begin using it from within the view controller.
Your view controller is actually retrieving the data but your model object is the one maintaining the data. In this way, you can pass on the model data with your twitter object to other view controllers.
Control over the application resides in the controller, so it is the object that will retrieve or save persisted data, update views with that data, and handle various events. Consider it the glue between the model and the view!
For example, if you were to click on a button to open a new modal view, you'd handle that event in your view controller. In the method that responds to the clicked button, you will create or access the new view controller and present it using presentModalViewController:animated:. If that new view and controller needs data that your current controller has access to, you could set a property in the new controller to refer to the object.

How do I access the managedObjectContext from a controller deep in the UI?

I'm still a little fuzzy on understanding iPhone/Cocoa in general so this is probably a simple question.
I have a CoreData Window-Based App for the iPhone. The rootController is a UITabBarController. The first tab view has a UINavigationController attached to it with a table in it's main view.
When the App starts the objectContext is set up, which makes sense to have the App do that once. But now I have the managedObjectContext in the main Controller but I want to get that passed down to the Controller of the View inside the navcontroller, inside the first item in the TabBarController's tab list. How do I do this?
Would naming the one of the fields in the UI Inspector Tool allow me to do something like:
tabcontroller.navcontroller.manageObjectContext = self.managedObjectContext;
Would this only work if the controller was instantiated and 'live'. (Do the controllers not get instantiated until they are needed?) What if this was in a view that was for the most part hidden?
Anyway this is probably a simple thing I'm just not understanding things properly yet.
What is the general right way to share the manageObjectContext that is created and setup in the rootController to the many sub-controllers in the app?
I'm guessing this is the preferred method assuming the core-data initialization is done in the AppDelegate:
[[[UIApplication sharedApplication] delegate] managedObjectContext]
I usually give controllers a - (id)initWithManagedObjectContext:(NSManagedObjectContext *)context init method and a corresponding variable.
If a controller creates another controller in turn, it will, if needed, pass the NSManagedObjectContext to that controller in the same manner.
If you don't want to create an extra init method, just give the controllers a property for the NSManagedObjectContext and set that property directly after creating them.
I usually try to limit the number of controllers that directly deal with and "know about" Core Data though.
The answers to this question provide several means of accessing the Core Data stack deep within your application. As I indicate in one of the comments, I prefer to use a singleton DatabaseController that can be accessed from wherever, similar to how the NSUserDefaults' standardUserDefaults works.

Using iPhone NSNotifications in MVC app

I have an NavigationController based app where the data model is stored in the appDelegate after being fetched using CoreData. The views that display the data all have a pointer to appDelegate so that they can query the model for the required data. They then call methods in the appDelegate in response to user selections.
My question is, doesn't the MVC pattern optimally hide the data from the view? Would it be best practice for the appDelegate (in this case serving as model and controller) to supply the data to the view, and for the view to simply send a notification when there is user input? That would eliminate the need for the view to maintain a pointer to the appDelegate.
You're correct to worry about AppDelegate taking on this role. AppDelegate is an easy place to dump stuff, and it quickly becomes overwhelmed with roles. As you note, it's playing data model, data controller and application delegate. If you're getting to the AppDelegate using [[[UIApplication sharedApplication] delegate], it's especially a problem because it makes it very hard to reuse the view in other programs. It's less of a problem if your view has a delegate ivar that just happens to point to the AppDelegate, but could easily point to some other object.
The view often should have a delegate that it passes UI events to. See UITextField and UITextFieldDelegate for a good example pattern. Views generally don't post NSNotification for this kind of stuff. Conversely, model classes typically work best without delegates in my experience.
I am a huge believer in NSNotification. My programs use them for almost all data that moves up from the model layer. But for actions that move down from the view layer, delegation and direct method calls typically work best.
Its not your view that accesses the data. The controller should be the link between the data and the view. If by view you mean the controller, then that's perfectly fine and basically what MVC is all about.