I'm asking what is probably a fundamental question but I'm relatively new to iOS and Object Oriented Programming:
Question:
If I have a simple app with 2 Views & View Controllers that receives input from each, how should I structure my program so that I can reference the values that were inputed in each view controller and take these values, add them together (as an example), and then output it to say a 3rd View Controller?
If this was all done in the same controller then I can do it but the issue is where can I store this so that the data is available across multiple controllers?
Fundamentally I'm not sure where the 'processing (addition) should occur. Should this be in another class, or AppDelegate? Is this termed the 'model' in MVC and can multiple View Controllers reference the same model?
I'm sure this is really basic stuff but an example or reference to material will really help clarify this for me.
Thanks
My personal pattern is a variant of the Model-View-Controller pattern. For each screen I have a ViewController, a UIView, a model class to hold data displayed by the view and a helper class to hold all of my business logic for that view. Those classes reference one another so that if you know one you can get to the others. I also link my model classes and helper classes in hierarchies that correspond to the controller hierarchy and that's how I solve the problem you describe. For a given view you can get to its helper and model class instance and from there you can navigate to any helper and model instance for the entire app.
There are probably better ways to do this, but that's how I do it.
You can use NSUserDefaults to store the shared data or you can implement your own shared storage object as singleton, here's one tutorial for creating singleton object in obj-c with demo project.
Related
I'm having multiple viewcontrollers (in a navigation stack or maybe not) and each controllers collects some data based on users input. In the end I need to use those data in the last controller.
So what would be the best approach/design-pattern to implement this scenario?
If you have (a navigation stack ) and you need to collect data from all this view controllers
the best way Not to use User-defaults , or Singleton
for your case you have to do container of view controllers to handle particular Process . Note (UINavigationController, UITabBarController, and UISplitViewController * is just containers)
For example create account that require 4 steps that collect users inputs , each step is represented by ViewController and at end you need to push all this data to API server ,
So create Parent ContainerViewController of child viewControllers and use Delegation to pass data after every step to ParentViewController , ParentViewController will be leader to allow next step and provide Data for this step . Here is how to begin create Your own Container managing-view-controllers-with-container
Do not use a Singleton
Singletons can be accessed directly from anywhere in the app. You have no control. a-lot coupling in your code and make your objects hard to test in the future
Do not use UserDefaults
UserDefaults is to store user preferences that persist between app executions.Anything stored there will stay until you remove, so it is not a mechanism to pass data between objects.
So you have to use architecture pattern in the future
Architecture
Each ViewController should only take care of their own screen . also viewController shouldn't know about each other. if we do this we will remove a-lot of coupling between our view controller classes.
So You can use Coordniator to be The leader that handle all navigation
Coordniator
and check how to Integrate with MVVM at MVVM-C
also Viper use this technique to handle navigation check Viper
Well, the best approach, in my opinion, is to use MVVM (Model-View-ViewModel) architecture pattern.
1) For each of your UIViewControllers, create a separate class (derive from NSObject if you wish) that's you consider to be the "view model" belonging to that UIViewController. Your view controllers are not allowed to access model classes... that's now a job of your "view model" classes.
2) Create a singleton named, say, DependencyManager. Your view models access it to obtain your models (or at least top-level models if they're hierarchial) and anything else they may need like networking services, etc... The DependencyManager acts as a way your unit tests can inject replacement "mock" versions of your models, network services, etc... when it's time to test each viewModel.
3) Create your model classe(s) that contain the data. Your view controllers had collected various data within the UI controls, and given that raw data to your viewModels. The viewModels may mutate that data (or not) and stick it into the appropriate models they acquire from the DependencyManager.
4) A ViewController is also allowed to ask their viewModel for data. So your last ViewController would obtain whatever data it needs from its viewModel.
Remember: ViewControllers should not directly manipulate your model classes.
Also, your ViewModels should never reference any UI objects, nor even have a reference to its ViewController.
Side Note: I recommend having each ViewModel conform to a protocol which derives from an empty protocol named something like "MockableViewModelProtocol". You can add a single property to your DependencyManager of type MockableViewModelProtocol that each of your ViewControllers can check before they create their ViewModel, in case a unit test has assigned a mock ViewModel for the ViewController to substitute. Another benefit of such a protocol is for quick & clear understanding of the relationship between a ViewModel and its ViewController. Often you'll have not just properties and methods but also callback properties (closure properties).
So there ya go. That, in my opinion, is the best way to not only design a way to manage and access data among a bunch of viewControllers, but to also test all your classes and their use of that data.
And unlike all the current claims that Storyboards block dependency injection and thus prevent testing of your ViewControllers, that's just bunk. By using a DependencyManager, such that your view controller tests inject mocks there, the tests get the same benefit as if they injected a mock directly into the ViewController, and yet the ViewController is still instantiated by the Storyboard. I've shipped a large app quite successfully with this approach.
There can be multiple ways to achieve that,
Pass the data along the controllers until the last one where you want to use it.
Use a Singleton to store the data from each controller. The only issue with Singleton is that it will persist in your app.
You can also store the data in UserDefaults. After using the data, you can clear it out from UserDefaults.
Let me know if you still face any issues.
In my opinion the best way to handle this would be to use a Flux design pattern. There is a framework called ReSwift on github that allows for unidirectional data flow similar to Redux.
Even if you do not use this framework specifically, the concept of unidirectional data flow is perfect for this situation. The state gathered from each view controller will exist outside of any view controllers, the view controllers will stay agnostic of each other, and there will be no need for any parent view controllers to act has higher order coordinators. Then on your last view controller, you access the state and use it however you need.
Another benefit is if you need to break out your view controllers into multiple view controllers through containers, i.e. one screen has 2+ view controllers, you will not need to create more infrastructure to pass data between those, which in my experience gets really messy.
I am designing a To Do List iOS application. I have multiple table views, which all modify the same to do lists array. Should I use a singleton class for the to do list array model, so that it is always up to date, no matter which table view is accessing it?
Would it be better to have a single main controller class (singleton) which contains the model data as well, and has some methods to interact with the data. Then have the other controllers call these methods on the main controller?
Yes, that's the idea of MVC. You have one model, and multiple views of it, with interactions orchestrated by multiple view controllers.
You do not have to make your model a singleton, although this is a very common approach in iOS development.
That would certainly be one way to do it, sure.
If you later decide you want to make your app document-based then you might need to go back and refactor, so think about future use first before you make your decision.
It's totally viable to do so, but you lose the flexibility of potentially having multiple model objects. I'd personally suggest having a singleton "manager" of some sort, that gets the model from your backend (or wherever it's coming from), and holds a reference to the model.
No, the model should not be a singleton. Singletons get overused. It's possible that you may wish to offer the user the ability to create more than one todo list. Maybe one for home and one for work, for example. Each list would be a separate instance of the todo list data model.
Using a singleton would prevent you from having more than one active todo list.
This question already has answers here:
Passing data between view controllers
(45 answers)
Closed 9 years ago.
I keep hearing two different sides to this story. I hear nsuserdefaults is the best way to pass data from one controller to another while using property is the best way. I got a bunch of answers on stackoverflow and now wondering what you guys think.
There is no best way to pass data, it really depends on the issue you are trying to solve. That being said dependency injection is the most common one, that is simply instantiating your view controller and setting the properties you need.
As a note NSUserDefaults most definitely should NOT be used to pass data between view controllers, it is meant to be used as a way to persist state between application launches. Maybe you are referring to a singleton object?.
While you certainly can use NSUserDefaults for passing information around your application, I find that it is often cleaner and more obvious to pass information explicitly whenever possible.
For example, if you have a view controller with a list of book titles and another view controller that gives you details about a selected book, this would be a straightforward way to pass information from one to the other:
BookDetailsViewController *bookDetailsViewController = [[BookDetailsViewController alloc] init...];
bookDetailsViewController.book = selectedBook;
One view controller has information, the other needs it, so the information is simply passed from where it is to where it needs to go.
If information has to go back the other way, from a presented view controller to its presenter, blocks or delegation can be utilized in a similarly straightforward way.
If information needs to be saved or used throughout an app, this is where NSUserDefaults and/or singletons can be useful. In general though, I would not put information in a place where it is not actually needed or used.
When I need to access the same data from different places in the application, I use a shared model object and use it as a singleton.
Every object that needs it holds a weak reference to it, and the object exposes an API to query its properties.
If you also need to persist the data in this shared model, make it implement the NSCoding protocol and add methods to serialize it to a file.
Let's say that I'm drawing a solar system inside of a single SolarSystemView (a subclass of UIView). Should my SolarSystemView have an instance variable of class SolarSystem (a class containing a data structure with all of the important planetary and stelar properties), or should that be under ownership of an instance of a SolarSystemViewController? Or is it something completely different? I can't find any example code that gives a satisfactory answer.
I imagine that if the view owned the model, operations would be very smooth, but that also doesn't feel like good style. After all, the SolarSystem instance has to change dynamically somehow, and with the same or similar rate that the SolarSystemView updates.
In the MVC paradigm, the Model is supposed to be separated from the View. To get the data it needs to draw itself, it asks the Controller for it, which in turn asks the Model for it. That way, we decouple the information from the GUI. Think of it as Model Controller View if it helps. Therefore, in most cases, the Controller "owns" the Model.
For example, in cs193p, the CalculatorViewController (Controller) has a CalculatorBrain (Model) property, which it interacts with to get the results of equations to display in the View.
In your particular example, the SolarSystemViewController would probably have a strong reference to the SolarSystem, which it would poll for data that it would hand off the the SolarSystemView so that it could draw itself when it needed updating. The SolarSystemView might also notify the SolarSystemViewController when the user interacts with it so that it can display other views or update the SolarSystem, among any other tasks it could perform.
Note that the MVC paradigm in Cocoa and Cocoa Touch is a little different than the more generalized version of MVC seen elsewere, like Smalltalk. For example, if you look at the Wikipedia page on MVC the diagram there should look different than what you've been learning. In fact, GoF (Design Patterns) describes MVC thusly.
MVC consists of three kinds of objects. The Model is the application
object, the View is its screen presentation, and the Controller
defines the way the user interface reacts to user input. Before MVC,
user interface designs tended to lump these objects together. MVC
decouples them to increase flexibility and reuse. MVC decouples views
and models by establishing a subscribe/notify protocol between them. A
view must ensure that its appearance reflects the state of the model.
Whenever the model's data changes, the model notifies views that
depend on it. In response, each view gets an opportunity to update
itself. This approach lets you attach multiple views to a model to
provide different presentations. You can also create new views for a
model without rewriting it.
In both these cases, the Model itself is contacting the View to update it. However, on iOS, interaction between the Model and the View are handled through the Controller. This is well explained in the first session of cs193p as well as Apple's own documentation on MVC relationships. That way, you only need to rewrite the Controller code to reuse the Models and Views elsewhere.
Here's the MVC diagram from cs193p to clarify.
In this case, SolarSystemView should not contain any instant of the SolarSystem class. The SolarSystemViewController should be the go between your view (SolarSystemView) and the model (SolarSystem).
Say I have a core data model with a few entities and, throughout the application's lifecycle, I'll be getting and setting properties of these entities from different views.
At least to me, it seems like passing the managed object context around from view controller to view controller and having the controller's code know about the different entities or derived objects goes against decoupling. As a matter of fact, there is a similar question where the only answer suggests passing around the managed object context.
So, in the spirit of MVC, I thought it might make more sense for view controllers to stick to controlling views, and have a singleton model controller through which these view controllers can query the model to feed their views and notify back when the model needs to be updated based on user interaction with the view.
But I'm not sure how to go about implementing the single model controller interface. For instance, here's a few ideas:
make this controller both a delegate and data source to all view controllers?
have the controller be only a data source and use notifications for updates?
kvc/o?
is the whole idea of a centralized bridge from/to the model just overengineering the MVC pattern? That is, is there some reasonable argument in favor of instead passing the managed context around and this not being considered crappy object-oriented design?
Another thing I'm thinking is, if the singleton controller is a delegate and data source, the methods to get model data and update the model should implement some sort of visitor pattern, right? Meaning, for example, if view controller A allows the user to interact with model entity / object A and some view controller B allows for the same for a model object B, and both view controllers rely on the same delegate, the delegate will have to have some way to know which model entity it should target depending on what concrete controller is calling on it.
I'd appreciate any thoughts / ideas
Your idea of a "model controller" makes sense, the problem is I think you may be thinking of it wrong.
It's not really a "model controller," it is the model. In MVC (traditionally), the views communicate with their controller for advice on what to do with interactions, and the controller decides what to tell them based on its logic and information it gathers from the model.
So really, you're simply abstracting the model further, which is a good thing. The idea of a "centralized bridge" is what you want in MVC. Your controller should see a high-level abstraction of the underlying model's implementation, as with all interfaces and layers of abstraction.
As far as implementing the model goes, I'm not sure which is the "best" way to do it, but I can't imagine using the delegate/data source strategy (or sending notifications to the controller) would be detrimental, I'd guess it depends on what you're comfortable with. If you love KVC/KVO, I doubt it'll hurt you. As far as the visitor pattern goes, the logic concerning keeping track of which controller gets which object should be within the model.
It seems to me that your biggest concern is with passing the model around from controllers. It is perfectly alright for the controllers to reference and use the model, but trying to manage their creation and penetration, you're right, is a bit much.
I'd recommend using a Dependency Injection container that can generate the access layer to your model. This way the controllers will get the model passed to them via DI, w/o having to pass around how to create the model classes.
The idea of a single centralized bridge isn't a bad one either, but it would make things a little tricky to unit test.
In the end, I ended up dropping the delegate / datasource approach for a singleton pattern:
I figured the following:
all controllers are collaborating with a single shared
managedObjectContext
the Core Data model itself revolves around a single "configuration"
entity (not the configurations that the Core Data literature refers
to).
This "default configuration" is also what all controllers interact
with, since all other entities exist in the context of this
configuration entity.
So essentially, I have a shared resource which should be accessible in a way similar to a global variable.
So I encapsulated the shared resource in a Singleton through whose interface all other controllers can query and modify the model without knowing much about the internals.
The general interface allows for things like:
[ModelControllerBridge sharedBridge] defaultConfiguration] which
returns the default shared NSManagedObject
[[ModelControllerBridge sharedBridge] newDataSample] which returns a
new NSManagedObject and internally allocates and inserts it in the
appropriate entity within the model.
[[ModelControllerBridge] shouldCommitChangesToModel] which signals
the context should be committed to the permanent store
etc.