Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Collecting data from multiple ViewControllers in Swift

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?

like image 535
Vineet Ravi Avatar asked May 12 '18 06:05

Vineet Ravi


2 Answers

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

like image 103
Abdelahad Darwish Avatar answered Sep 19 '22 04:09

Abdelahad Darwish


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.

like image 44
Smartcat Avatar answered Sep 21 '22 04:09

Smartcat