Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVVM generic networking architecture

I develop iOS applications using the Model View ViewModel paradigm to structure my view controllers and to represent their data. That in conjunction with ReactiveCocoa is a powerful tool; view controllers become less bloated, view models are easier to test, and there's a clear separation of concerns.

The one problem I have with this particular architecture is that like MVC, there's still not a clear place or way to structure networking code. Take the following trivial example:

class HomepageViewModel {
    var posts: MutableProperty<[Post]> = MutableProperty([])

    func fetchPosts() -> SignalProducer<[Post], NSError> {
        return SignalProducer { observer, disposable in
            // do some networking stuff
            let posts = ....
            observer.sendNext(posts)
            observer.sendCompleted()
        }
    }
}

Then in my view controller somewhere I can do:

self.viewModel.posts <~ self.viewModel.fetchPosts().on(next: { _ in self.collectionView.reloadData() })

To me it feels like the whole point of using MVVM was to not expose the views and view controller (what I call the view presentation layer) to any networking code whatsoever but I still need a way to be able to observe that new content has been fetched without knowing the specifics, just that a successful fetch occurred. I'd imagine that would look something like:

self.viewModel.contentUpdatedSignal.observeNext { _ in self.collectionView.reloadData() }

At the same time, I also don't want to lose the ability to bind signals and signal producers to mutable properties on my using <~.

class ViewModel {
    let someProperty = MutableProperty<[SomeModel]>([])
    var (contentUpdatedSignal, observer) = Signal.pipe()
    init() {
        self.someProperty <~ self.fetchContent().on(next: { _ in observer.sendNext() }
    }

    func fetchContent() -> SignalProducer<[SomeModel], NSError> {
        // do some fun stuff
    }
}

This method of doing this is a little better but it still uses side effects to send a next event on the signal observer and if you're using a common ViewModel base class, you have to expose that observer so that subclasses can use it.

I'm looking for any improvements that can be made to the MVVM architecture, either changes to the architecture itself so that it's no longer MVVM and facilitates networking in a better and more generic way or even some sort of generic base protocol for view models that abstracts the whole process away.

For me, being generic as possible while exposing as little info about the view model as possible is key. Ideally, I'd like to have each view controller interact with the view model in exactly the same way in regards to how networking works.

EDIT:

Per at @lonut's suggestion, I moved some of the networking code to my model classes but only as static methods:

import Foundation
import ReactiveCocoa
import Moya

protocol RESTModel {
    typealias Model

    static func create(parameters: [NSObject: AnyObject]?) -> SignalProducer<Model, Moya.Error>
    static func find()  -> SignalProducer<Model, Moya.Error>
    static func get(id: String) -> SignalProducer<Model, Moya.Error>
    static func update(id: String) -> SignalProducer<Model, Moya.Error>
    static func remove(id: String) -> SignalProducer<Model, Moya.Error>

}

extension RESTModel {
    static func create(parameters: [NSObject: AnyObject]? = nil) -> SignalProducer<Self.Model, Moya.Error> {
        return SignalProducer.empty
    }
    static func find() -> SignalProducer<Self.Model, Moya.Error> {
        return SignalProducer.empty
    }
    static func get(id: String) -> SignalProducer<Self.Model, Moya.Error> {
        return SignalProducer.empty
    }
    static func update(id: String) -> SignalProducer<Self.Model, Moya.Error> {
        return SignalProducer.empty
    }
    static func remove(id: String) -> SignalProducer<Self.Model, Moya.Error> {
        return SignalProducer.empty
    }
}

This way models can implement network calls at will which has the benefit of abstracting away the implementation details such as specific Moya network calls, mapping of response objects, etc.

Imagine you have a User model:

User.get("myUserID")

It doesn't entirely solve the problem of how the view controller and the view model should interact with each other but it definitely moves the network code to a single point of failure.

like image 722
barndog Avatar asked Mar 08 '16 11:03

barndog


People also ask

What is the difference between MVC and MVVM?

Whereas the MVC format is specifically designed to create a separation of concerns between the model and view, the MVVM format with data-binding is designed specifically to allow the view and model to communicate directly with each other.

Why MVVM is better than MVC?

The MVVM pattern presents a better separation of concerns by adding view models to the mix. The view model translates the data of the model layer into something the view layer can use. The controller is no longer responsible for this task.

What is the MVVM design pattern?

Model-View-ViewModel (MVVM) is a software design pattern that is structured to separate program logic and user interface controls. MVVM is also known as model-view-binder and was created by Microsoft architects Ken Cooper and John Gossman.

Is MVVM clean architecture?

Note: You can combine Clean Architecture with the model-view-presenter (MVP) architecture as well. But since Android Architecture Components already provides a built-in ViewModel class, we are going with MVVM over MVP—no MVVM framework required!


1 Answers

I'm not very advanced in using MVVM or RAC but from what I've played with the networking code should be in the model part, then there is method that observes the results in the view model part (something like "fetchPosts()") and in the view part the fetchPosts method is called. I recommend you this blog post for a lot more information.

like image 78
Ionut Avatar answered Oct 13 '22 11:10

Ionut