I am working on a simple app to try out MVVM in iOS. I have only two screens in the app. Screen A displays content and the user can tap on settings button to pull up screen B. Screen B allows the user to change settings which affect the way screen A displays the content.
How should I communicate changes made in screen B to screen A so that screen A can re-display the content based on the setting changes? In MVC, I would use protocols/delegate to communicate changes made in ViewControllerB to ViewControllerA. Since view controllers have direct access to the models, I can just pass the updated model in the delegate call. In MVVM, would the communication happen between ViewModelB to ViewModelA or ViewControllerB to ViewControllerA(like MVC).
If the communication is happening between view controllers, what is ViewControllerB really passing to ViewControllerA? ViewModelA? If so, doesn't ViewControllerB knowing about ViewModelA defeat the purpose of reducing view controller responsibility/knowledge?
Thank you for taking time to read the question!
In a situation like this, I would engineer the data stream as follows:
Both ViewModelA and ViewModelB communicate with some entity from a model layer (the first M from MVVM). Frequently such entities are called "services" and contain persistence/network logic.
When the user opens ViewControllerB and changes some settings, ViewModelB makes respective changes to the model.
ViewModelA observes changes to the same model (via KVO, NotificationCenter, reactive publishers or a good old delegate pattern, whatever suits you best) and reacts to them by updating ViewControllerA if needed.
This way neither of your view models need to know about each other, and your model layer acts like a single source of truth for both of them.
Related
This is a design pattern question.
I have a 'framework' I'm building, and depending on the current displayed ViewController the framework needs to inject a UIButton into that ViewControllers view.
I don't want the ViewControllers to 'know' explicitly about the 'framework', so I was initially using the delegate pattern but that required a 'over-seer' to ensure that when only specific view controllers were loaded, that they knew about the 'framework'.
However, it occurred to me if I could, via a protocol, ensure that all delete ViewControllers fired off the same two custom NSNotifications i.e. subscribeToRequestButton and unsubscribeForButton, then the 'framework' would listen out for those, and upon receiving them, have the view controller object passed to it (via the Notification) so that it can inject the button.
Thereby preventing the view controller from knowing about or having a reference to the 'framework'; it just knows, if it want's that button, to fire those two notifications, and ensure that it implements a method for when that button is touched.
Is this possible or is there a better approach?
The key problems are that
a) I cannot have the View Controllers that need the button know about the framework; as they never have the opportunity to have a reference to is passed to them by some manager class, and
b) The framework doesn't know about the view controllers existence until it receives a notification that is needs to inject a button into something.
EDIT:
I'll just use a singleton pattern with a few public accessor methods for passing data in or querying.
As the title states, I'm looking to simply pass the information.
I have a tabbed view application currently, and the user inputs data into a text field, presses a button, then labels are filled with the entered text on the same viewcontroller.
I want to send that information to the other tab and fill a label.
I know I can do this via protocol or segues, however, I want to remain on the current tab. I haven't seen this as an example anywhere, only to switch the view to the other screen.
Anyone know how to simply pass the string entered and not change the view?
You are looking at this all wrong. You do not want or need to pass data between view controllers. If your app makes proper use of MVC (model, view, controller), then what you should be doing is updating a model. That model should broadcast that is has been updated. Anyone that cares about the model should react to those notifications as needed.
You have a tab controller with multiple view controllers. Two or more of your view controllers have an interest in the same data model. Both should reference the same instance of the data and be setup to be notified about changes to that instance of the data model.
One view controller, through its views, updates the data model. The data model then sends out a notification that is has been updated. Now the interested view controllers receive this notification and update their own views based on the updated data model.
No view transitions required. No segues required. No communication between different view controllers required.
Look at the documentation for NotificationCenter for ways to broadcast messages and for ways to listen for such messages.
I am coming from a C# wpf background where you can have one ViewModel handling multiple Views. This is a great way to share data amongst unrelated views. However I can't figure out how to do the same thing in iOS, as you seem to need a Controller for each View.
What I am trying to achieve is to have a sign up sequence where the user populates 5 screens of data one by one. I was going to use a PageViewController for this and each click on Next would transfer them to the next page in the sequence. All the while, adding all their input data to a parent model object which stayed around for all five screens, at the end you can submit the whole lot to the database to sign up.
The only way I can see so far to do this is to create five separate ViewControllers, one for each screen of the sign up, and create the navigation logic to display them as you click through. However this a) seems overkill and b) means each subsequent screen and viewcontroller doesn't know about the information the user entered in the previous steps.
What is the correct way to do this in iOS?
You can do it in many ways. If you like to use UIPageViewController, you can actually have one view controller for all steps of the sign up process.
A main view controller would implement protocol UIPageViewControllerDataSource. It has two required methods, which return an instance of a content view controller. These instances can be of any UIViewController subclass, so you could have five separate view controller classes or you could have five instances of the same class.
Xcode has a project template "Page-Based Application". It might be a good sample code for you. There is one class DataViewController and it is instantiated for each page. If your case is really simple then this might be the best solution.
If you use multiple view controllers, you can pass data between them by overriding the method prepareForSegue(segue:sender:). The UIStoryboardSegue object has access to the destination view controller, you can cast it and set up the properties. (I am assuming you are using storyboards and segues.)
Another approach would be not to use UIPageViewController and implement the whole process within one view controller. There could be one view with five container subviews. Initially the first one would be shown, then the second one, and so on. This requires manual implementation of navigation between those steps, but gives a lot of flexibility. It's also more aligned with MVVM, because there is one-to-one mapping between a ViewModel and ViewController.
In general, iOS apps have lots of view controllers. It doesn't seem an overkill for me to use five in your case. It might be more code, but it's less complexity.
I am trying to develop a simple app to understand MVVM but I don't get it really well. I've read some articles, but they are a bit different:
View in different class MVVM
In this example the view is not the ViewController itself, it is a class that contains all the details of the view and it is an abstraction for the VC.
The ViewController creates the ViewModel with some data the it retrieve from DB, Network..
The ViewController talks with the model and manage the ViewModel to update the view.
ViewController is the view MVVM
In this example, the viewController is the view, it does not need other class. The viewModel is not created by the viewController, and the ViewModel of the next ViewController is created by a ViewModel. The ViewModel also has an instances of the Model, so if it needs to retrieve some data, it has instances of the DB, Network..
I don't understand well some things. If the viewModel is retrieving data from Network with asynchronous tasks and I want to display an spinner and get data for fulfil the view. Should I user blocks, KVO or delegates? If I use delegates, the view implements ViewModelDelegate Methods, would be that correct? Am I coupling view with viewModel? I am confused at this point.
If I have a firstViewModel that creates a new secondViewModel for a new pushed SecondViewController,and FirstViewController has to update its view before the user pops the pushed SecondViewController. In this case firstViewModel has to observe secondViewModel and notify the changes to firstViewController once popped, doesn't it?
I also have a question about ViewController creation. ViewControllers are the attendants of next ViewControllers creation? If I create an object that manages ViewControllers creation and navigation should I pass this object over viewModels?
I know they are very similar, but I am confused about which way I should implement and how. There isn't much information and examples about iOS MVVM.
I am not using Reactive Cocoa yet because I want to understand this pattern well first. Though it is in my TO-DO list.
Thank you!
It really depends on why you're using MVVM. I'm doing it right in the middle of the two cases you mention, actually. Here's the two reasons for MVVM usage:
First, you may be using MVVM because you want to organize your code in a way that pulls the logic out of the VC and keeps it clean. In this case, you're using a ViewModel in good OOP form and it's perfectly find to have the VC create it.
On the other hand, you may be intending on UI testing your VC's and therefore want to introduce mock ViewModels. In this case, you want to be handing the ViewModel to the VC. (i.e. dependency injection)
In my view, either is justifiable. You might even have multiple needs in one app. Therefore, I keep things flexible. My approach is to create the VM in the viewDidLoad() method of the VC only if it doesn't exist yet. This allows me to use my normal VM and to do create it (neatly) in my VC, but if I ever need to override it (unit testing, UI testing, alternate view style), I can, too.
The issue comes from the fact Cocoa apps, are by definition using MVC, and you have to try and make MVVM work with the underlying ViewController structure.
I haven't looked at MVVM in iOS, but have done a bit using Xamarin.Forms, where the View is just the XAML code, the view model interacts with the View and the Model. Under Cocoa apps, it doesn't really matter too much how you do the ViewModel part, but looking at your links, I think the second approach is closer to a purer MVVM approach.
I suggest you download the code in the link and have a look to get a greater understanding?
With iOS 6 Apple added state restoration to UIViewController and related classes. This allows the application to save state when terminated and restore it when the user resumes the application.
Everything seems to work fine, however I have reached a weird scenario which doesn't want to fit into the pattern.
Let's assume we have two view controllers, ViewControllerOne and ViewControllerTwo, both of them store some arbitrary state that is restored successfully. Now let's imagine that ViewControllerOne has a delegate property and that ViewControllerTwo is that delegate (which is a common pattern for modal view controllers). Who is responsible for restoring this relationship? And how is it supposed to be stored/restored?
In my particular case no storyboards are involved, the restoration happens in code, via the restorationClass property. My first instinct was to try and restore the relationship whilst creating the view controller in the restorationClass, however as the restorationClass has no idea of other existing controllers it can't quite restore such a relationship.
Alternatively, if it is the view controller that declares the delegate property, that is supposed to restore the relationship, then how does it now of the controller instance that was restored in some other class?
In short, this seems like a poorly documented scenario and I was hoping someone could shed some light on it.
I would say, the task falls on the delegate view controller to set itself as such, just like you do it before pushing the other view controller.
On how you can accomplish this, you have several options.
You can store a weak reference to your view controllers in a globally accessible location (for example, the app delegate), and use these values in application:didDecodeRestorableStateWithCoder: to set the delegation - this is what this method is for in the API.
Alternatively, you could post a "hereIAmThisIsMe" notification (with self part of the user info) from the top view controller to which the delegate listens and sets itself as a delegate.