Search code examples
iosswiftmvvmviewmodel

How to communicate between the different pieces when using MVVM in iOS


I did my research but because I don't have enough experience I don't know what is the best way to implement communication of the UserManger class when using MVVM design pattern.

So my timer app that got sync to the Cloud. So far I'm having HomeViewController, TimerViewModel, TimerModel, UserManager.

The UserManager object is taking care of authenticating User and also of communicating the authentication state to other classes.

The states are: .notSigned, .signing and .signed

Here I would need more explanation. To which object should the UserManager communicate the updated authentication state? Is it owned by the view controller or view model?

And another question. I don't know if UserManager suppose to communicate only to one class or is it ok to talk to many objects at the same time. I think that most of the objects simultaneously need to know if the user is signed or not. If this is true, what kind of communication do I implement?

For one to many, I'm thinking to implement the Observable protocol described in this article. If necessary I can copy the code instead of the article link.

I hope I described it well. I will appreciate any kind of answer. Or any new insights, big picture ideas about how to set up object communication throught the app. Do you know any good article about that?


Solution

  • All the business logic should be inside the ViewModel. The ViewModel should be the thing that invokes any method inside the UserManager. The UserManager should return the result parsed as the Model to the ViewModel. The ViewModel then needs to format the data and alert the ViewController to the event/data/network completion etc.


    Examples of formatting the data:

    • Let's say a networking request gives you firstName and lastName. But you only have 1 label to display the full thing. The viewModel should add those strings together (with a space) and provide a fullName property to the VC.
    • If you got back a Double, but need to format it into currency and display a symbol.
    • Taking a timestamp and formatting to a date string.

    These are the kinds of things that a ViewModel should do. So that they can be tested independently of the UI code and lifecycle.


    Communication:

    You can use the standard Apple API designs and create a ViewModel protocol, with a delegate and a callback. Each VM would confirm to the protocol, VC would set itself as the delegate of its VM and run your code inside the VC's delegate callback function.


    Naming:

    The word "Manager" has fallen out of favour because it is typically a default word used when the use of the class is unclear. For this reason it has a thousand different definitions. A class responsible for making networking requests and/or storing state should be called a "Service". i.e. "UserService".


    Sharing state:

    I'm probably going to make a lot of people angry with this one. But when state needs to be shared across multiple VC's, components, VM's etc. I follow the design patterns / architecture that Angular has setup. I make my service classes singletons. Have them store the state. Then Each ViewModel has one source of truth for each item and can format it at will.

    Other people use frameworks such as RxSwift and use Observables and similar technologies. My personal opinion is that this is tremendous overkill, involves a huge learning curve, means adding a lot of big libraries and brings in a lot of risk to the project. I prefer to stick to the patterns already available and working in the eco-system and keep things simple.


    Edit: one-to-many:

    It can depend on your use case and architecture decisions. I've found it possible to avoid the one-to-many issue in the majority of my uses cases, by having the singleton store the state and have the VC trigger something in the ViewModel on each call to viewDidAppear. Each time the VC opened, it got the latest data and continued on. The reason I bring up avoiding the one-to-many is that in the majority of cases there is only 1 VC on screen and running code at any point in time (using childViewControllers is the exception). I've found it possible to just side-step this issue entirely and keep the apps simple by changing how I interact with the data, rather than changing the structure of Apple's APIs.

    Now depending on your use case, this might not work if you have multiple timers and lots of screen switching, there could be a chance to miss an event.

    NotificationCenter is not my favourite approach, but if I had a one-to-many situation I couldn't avoid, I would give it a go. As mentioned in the article there are some downsides with swift features you can access through this means. It at least has the added benefit of being something that's been around for a long time and is well understood and unlikely to change much. There won't be any huge learning curve if other developers are working on this app.

    The other approach mentioned in the article is interesting, building your own stripped down version of RxSwift to add Observers into the project. My problem at the minute with RxSwift is when people don't use it in conjunction with singletons or some sort of central point of control. When you have an Observable on object A, Observed by B that also has another Observable Observed by C, which calls a function on D which is Observed by E, F and G .... and so on. This style of code is incredibly messy, hard to read, error prone and difficult to debug. So long as that is avoided and its the singleton that has the observable and all the ViewModels are observing, I think something like that would be OK if you are willing to put the effort in to build and maintain it.