to thank the organizers for such a nice conference so far and for the great talks that are still to come. Also want to thank you all for coming to listen to me talk about MVVM, RxSwift and how we use all of this at Brewbot. MVVM, RxSwift and DataControllers Esteban Torres - @esttorhe, NSSpain, 2016
through what's the current state of afairs when it comes to architecturing an app (just some examples though) First let's add some context Esteban Torres - @esttorhe, NSSpain, 2016
a gigantic pile of code embedded in the controller; which doesn't fully controls the model or the view but handles pretty much everything between heaven and hell And here comes the joke that should make an appearance in every presentation that references MCV; we end up with Massive View Controllers (this is your cue to laugh !). What's the «Problem» of MVC? Esteban Torres - @esttorhe, NSSpain, 2016
covered with MVVM (FYI I'm a HUGE believer of the benefits of MVVM) which help us separate even better the concerns of our app with a better defined architecture. We all know the old «View - ViewModel - Model» concept but if for some reason you don't we'll cover the basics here. MVVM Esteban Torres - @esttorhe, NSSpain, 2016
our data, the purest object/ struct in our codebase, it shouldn't be much more than a container/representation of the data that's coming to us from our datasource, wether that's a DB, a server, etc it doesn't matter. Model Esteban Torres - @esttorhe, NSSpain, 2016
«nothing more» than a bridge between the model and the view, this way we can manipulate our «raw» data to a more presentable and better structure representation of the model so that the View can present it nice and pretty to the user without the need to actually compute or format anything. ViewModel Esteban Torres - @esttorhe, NSSpain, 2016
and we are ready to roll our app. Everything makes perfect sense here, you go to your Storyboards, XIBs or via code (I'm not going to start a #holy-war about it) and create the visual containers for the information that we will be presenting to the user. For this presentation's sake we will use MVVM and the data will come from an API on our server. What now? Esteban Torres - @esttorhe, NSSpain, 2016
using structs here !) based on a JSON response from the server or based on the DB schema. Add some Models struct Hop { let uuid: String let name: String? let amount: Double } Esteban Torres - @esttorhe, NSSpain, 2016
nice bridging class that will take our Model and will format it nice and pretty for the user's consumption. OK, everything is taking shape, we are following one of the patterns that all the cool kids are using this days and then comes the part where we need to consume the data from the API. And a pinch of ViewModels class HopsViewModel { // Model private let model: Hop // Properties var name: String { return model.name ?? "N/A" } var amount: String { // Notice how here we should convert to the user's unit (imperial / metric) return "\(model.amount) g" } init(withHop hop: Hop) { … } } Esteban Torres - @esttorhe, NSSpain, 2016
using NSURLSession or Moya or AlamoFire, you can choose your poison. And we put it out there kind of like a helper function (because come on people, we have «Model, View, ViewModel») the pattern is not called «Model-View-ViewModel-Network». Network layer Esteban Torres - @esttorhe, NSSpain, 2016
in there, let's say yes. Most of us that have coded at least 1 app have done this so yeah. Let's throw the folder of networking and put our class there and quick and easy we have everything ready. We have a really nice separation of concerns… do we? Technically speaking our models should be agnostic in terms of where the data comes from so… the JSON parsing shouldn't be inside of it… (Although I'm guilty here, I like having my JSON parsing per class !) Esteban Torres - @esttorhe, NSSpain, 2016
the real network call will happen on our network layer but then again; who should be responsible of telling our network layer to hit the server? Where do we ! things now❓ Esteban Torres - @esttorhe, NSSpain, 2016
response to the MVVM so that everything gets parsed? Don't know about you but that sounds to me like a convoluted mess and I think we are violating 1 or 2 «laws» of the separation of concerns and committing 2 or 3 atrocities against nature. ! Esteban Torres - @esttorhe, NSSpain, 2016
we clean up our ViewControllers, we signal our ViewModel from the VC and the VM signals our network layer and then we process everything on the ViewModel and throw that back to our VC where the View will take charge of displaying the data to the user ! Esteban Torres - @esttorhe, NSSpain, 2016
get the data from our source, parse it to generate our models and then format it… sounds like we are putting too many responsibilities unto our VM (not to mention that we are «creating» the models from the viewmodel which in theory is a bridge, in this case a bridge from nothing to the view but in the middle it builds the other side of that bridge while being the bridge itself… ! exactly) the and we are moving from «Massive View Controller» to Model-VeryBigViewModel-View (for the love of god laugh here because I couldn't come up with a better «joke» ") MVVM …an abstraction of the view exposing public properties and commands… In the view model, the binder mediates communication between the view and the data binder. The view model has been described as a state of the data in the model.1 1 http://wayback.archive.org/web/20080201101909/http:// www.acceptedeclectic.com/2008/01/model-view-viewmodel-pattern-for- wpf.html Esteban Torres - @esttorhe, NSSpain, 2016
of Data Controllers so I will… although it was actually our CTOwho implemented them. When I first joined Brewbot the DataControllers where already there and they just made sense (mind you we didn't have MVVM back then though) Like I said earlier I'm a proponent of MVVM and first thing I mentioned when I joined was that we should start using it; it would help us clean up the code and will make things easier to maintain and pretty much recite the whole part of Google's-great-reasons-to-use-MVVM and his response was this: DataControllers Esteban Torres - @esttorhe, NSSpain, 2016
can understand just a certain part of it and some of the operators still elude my comprehension but that doesn't stop me from understanding its benefits for a certain type of projects and didn't stop me from realizing that our app at Brewbot was one of those projects. The framework that you choose won't matter much in the big scheme of things… just read carefully each one and go with the one you feel most comfortable with. Back in 2011 or 2012 I was flying to a certain gathering of developers called dub-dub and I bought a book for the flight. The name of the book was `Functional Reactive Programming by Ash Furrow. In this book he explains fundaments about FRP and also ties everything together with the use of MVVM; that was my first approach at MVVM and I was sold. More importantly at the end of the book there's a chapter about ReactiveCocoa and MVVM with a μFramework called ReactiveViewModel. I was using ReactiveCocoa at the time so everything started to make sense in my brain and I decided to give it a try and it was great. (not so much though, had to write Ash a couple of emails and bug him on Twitter to clarify a couple of things, but him being the great human being that he is answered every one of my questions and helped me understand a topic which feels me with passion One more thing… Esteban Torres - @esttorhe, NSSpain, 2016
there like Krunoslav Zaher created RxSwift and RxCocoa. I liked the idea of sharing concepts with other languages (RxJava, Rx… <add more>) and that cause the creation of RxViewModel. And that was a HUGE introduction to tie with my talk. The age of Swift Esteban Torres - @esttorhe, NSSpain, 2016
too clear what Reactive Programming is, let's just think that data is a stream and you can tap unto some sinks and «listen» to that stream. So whenever new data comes down the stream you'll get it almost instantly and you can «react» (get it?) to it. It's a really nice way to bind everything together, for example a login form can be easily «glued» with Reactive programming so that the validation happens reactively and without having to go all over the place checking for each data that we need. RxSwift Esteban Torres - @esttorhe, NSSpain, 2016
of this, worked in a way but was kind of slow and everything came down the same sink, you had to select what went where and when we should do something depending on which event was firing Esteban Torres - @esttorhe, NSSpain, 2016
RxSwift and RxCocoa. This help us detach some of that «funnel like» pattern and instead separate the different streams based on the signal that emits them. Esteban Torres - @esttorhe, NSSpain, 2016
As you can see our VCs hold a reference to a ViewModel for that specific view, this VM is of the reactive type so we hook up the active signal in order to correctly react to our active state change. Inside of it we hold a reference to a data controller specific to the task at hand: Esteban Torres - @esttorhe, NSSpain, 2016
that's in charge of actually calling our netwokr layer (we don't need to dwell into the network layer, pretty basic-bolierplate-y code there. In our Data Controller we handle the responses from the network layer and bubble them up to the VM after correctly parsing it and creating the correct models that are needed. Now we are back at the VM where we just need to format our data accordingly to our specification, handle some of the pagination logic (since to me at least that's tied to the presentation layer given that you need to «format» the data accordingly to the page you are currently in) and fire up the results on the appropiate signal. Depending on your logic you can defer turning off the signal either at the very beginning of the chain or just before returning a set of results or errors. class ViewModel { // Hold a reference to our DataController private let dataController: DataController // Hold a reference to the model we will be getting private var model: Model? func getModel(id: Int) -> Observable<Model> { return Observable.create { observer in dataController.getModel(id) .subscribe(onNext: { [unowned self] model in self.model = model // If we need to format something else we should do it here observer.onNext(self.model) }, onError: { error in // Maybe properly format the error to the user here as well observer.onError(error) } ).addDisposableTo(disposeBag) } } } Esteban Torres - @esttorhe, NSSpain, 2016
VC which, depending on how you connected everything needs to either reflect the changes from the VM into the UI or simply do nothing because everything is tightly connected via Rx logic. And if we go back to the VC we can update our UI and be done with our lives. class ViewController: UIViewController { // Retain a reference to our ViewModel private let viewModel: ViewModel func loadData() -> Void { self.viewModel.loadModel() .subscribeOn(MainScheduler.instance) .subscribe(onNext: { [unonwed self] viewModel in // Update your `UI` accordingly self.nameLabel.text = viewModel.formattedName self.amountLabel.tet = viewModel.hopsAmount }, onError: { error in } }) .addDisposableTo(disposeBag) } } Esteban Torres - @esttorhe, NSSpain, 2016
they are just «automagical» ways to connect to parts, they could be objects, structs, UI components and since they are connected they react as soon as one changes (or both depending on how you bind them). Rx Bindings Esteban Torres - @esttorhe, NSSpain, 2016
don't really detail where we should put the responsibilites of calling the network. We covered why/how DataControllers help us clean up that mess by takinng up the role of actually handling that portion of the flow. We glued everything together with a «reactive» approach to better handle an app with constant and multiples inputs of information that need to be presented to the user. What did we accomplished? Esteban Torres - @esttorhe, NSSpain, 2016
acceptance tests, test all the things!! Last time I gave this talk I was asked how did we test this approach Testing Esteban Torres - @esttorhe, NSSpain, 2016
DI approach. That way you can actually change or stub your behavior to be able to actually test it in a quick and easy way. How do we test this? Dependency Injection Esteban Torres - @esttorhe, NSSpain, 2016
a little bit of «complexity» due to the «extra» layers. Using a reactive framework is a though choice because makes the curve for new developers joining a little bit harder and it will also «bleed» through all the layers, then changing or doing major upgrades is a painful. Are there any Cons? Esteban Torres - @esttorhe, NSSpain, 2016
have created yet another architecture or to have patched MVVM in any way. ^ I thought that our solution is nice enough and works good enough to be shared with the world. Why? Maybe there are some other devs out there asking themselves the same questions about where to put certain calls, maybe this will be for them like an epiphany and they'll be like: Conclusion Esteban Torres - @esttorhe, NSSpain, 2016
just wanted to give you some brain food and maybe pique your interest in adventuring in using a reactive approach next time you people need to tackle a new challenge. . "That was the worst idea EVER… BUT seeing how badly they tackled that problem gave me an idea about how to actually make it work." Esteban Torres - @esttorhe, NSSpain, 2016