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.
Related
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.
I am creating an Obj-C application, and am working on a single view. However, within this viewful, there is enough logic that I want to separate that from the view controller itself. I decided to create a delegate, but my current problem is how these two should communicate. Two potential solutions I have been considering:
1) An instance variable of the view controller within the delegate. Then when any view updates need to happen I simply call the appropriate method on that instance variable.
2) Notifications being fired off by the delegate and add the view controller as an observer.
Is there one method that is obviously superior or does it depend on the situation? I don't know the proper way to go about this and am trying to approach this from the correct angle.
If it's non-UI related responsibilities you want to separate from the view controller, it's very natural and recommended to have another object responsible for those tasks. In this case, you can either have a singleton for those responsibilities, or simply a NSObject subclass instance strongly referenced in your view controller.
If it's pure UI related tasks you want to separate from the view controller, just to organize the source code clearer, you should instead create categories for the view controller and name the categories according to their responsibilities, for example, MyViewController(Localization), MyViewController(UserInteration), etc.
This is a broad question. However, if what you are trying to do is simply refactor existing code to make maintenance easier without changing any functionality, use instance variables. Your code execution sequence will be unchanged and your method can return status information on the execution results. Your original controller and your new refactored class are tightly coupled - know a lot about each other.
Notification logic is event-based (something interesting happens and an event is sent to registered observers). The logic is executed when the observer receives the event so there is no guarantee you can control the exact execution sequence, and the caller does not know if the observer received or successfully processed the message - the are loosely coupled.
When in doubt look at what Apple does in its framework classes like UITableView. Delegates give you fine control over when things happen.
I am creating a login app for iOS and have stumbled upon a design issue. In my app some data (e.g. current user id) has to be shared across view controllers so my initial thought was to hold that data in the AppDelegate and just call it from there whenever it was needed. This would work but it would mean that the AppDelegate would be doing work that it wasn't intended to do and hence it would be bad architecture, so I thought of creating a singleton class to hold all the shared data instead. This seems to be more focused and I have used this strategy in some of my previous apps, but googling the best practices for singleton classes, I found many results against using them, as they:
Hide dependencies
Violate the single responsibility principle
Make unit testing difficult
(taken from the top answer to this question: What is so bad about singletons?)
The alternative to the two other design patterns would then be to pass data back an forth between view controllers, but this again seems inappropriate. Take for example the current user id I would have to pass it from the login view controller to the main view controller (which is the same for all users), then from the main view controller to a more specified view controller which is customized for the user. In this case the main view controller is holding data only for the purpose of passing it to the more specific view controller and to me this also seems to violate the single responsibility principle. Would it be bad design to let the main view controller hold data it doesn't need or is there a better alternative?
There is no "magic" way of storing the model state of your model-view-controller application: you build a class, and you "anchor" it somewhere for easy access.
One common practice is using the app delegate. It is convenient, because it is a system-supplied singleton globally available to your app, but it is logically inferior, because, as you noted, app delegate was not intended for that.
Another common strategy is using a singleton. This is commonplace in app development, and it works fine in practice. You can mitigate the common grievances by limiting the access to your singleton to initializers of your view controller - this addresses the hidden dependencies and the testability issues.
Finally, you can pass the data around, in which case it is the same as "anchoring" it in one of the view controllers. Arguably, this leads to even bigger problems than singletons, because the dependencies become even harder to trace.
I would recommend storing the state in a singleton, and use it in the initialization code of your view controllers. This way the dependency on the singleton remains localized, and you could test your logic with minimal effort.
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.
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.