Didn't get a chance to attend the Realm SwiftUI Property wrappers and MVI architecture Meetup? Don't worry, we recorded the session and you can now watch it at your leisure to get you caught up.
Realm SwiftUI Property wrappers and MVI architecture
In this second installment of our SwiftUI meetup series, Jason Flax, the lead for Realm's iOS team, returns to dive into more advanced app architectures using SwiftUI and Realm. We will dive into what property wrappers SwiftUI provides and how they integrate with Realm, navigation and how to pass state between views, and where to keep your business logic in a MVI architecture.
Note - If you missed our first SwiftUI & Realm talk, you can review it here before the talk and get all your questions answered - https://youtu.be/mTv96vqTDhc.
- In this meetup, Jason spends about 35 minutes on
- StateObject, ObservableObject, EnvironmentObject
- Navigating between Views with state
- Business Logic and Model-View-Intent Best Practices
And then we have a full 25 minutes of live Q&A with our Community. For those of you who prefer to read, below we have a full transcript of the meetup too. As this is verbatim, please excuse any typos or punctuation errors!
Throughout 2021, our Realm Global User Group will be planning many more online events to help developers experience how Realm makes data stunningly easy to work with. So you don't miss out in the future, join our Realm Global Community and you can keep updated with everything we have going on with events, hackathons, office hours, and (virtual) meetups. Stay tuned to find out more in the coming weeks and months.
To learn more, ask questions, leave feedback, or simply connect with other Realm developers, visit our community forums. Come to learn. Stay to connect.
Jason Flax: Great. So, as I said, I'm Jason Flax. I'm the lead engineer of the Realm Cocoa team. Potentially soon to be named the Realm Swift team. But I will not go into that. It's raining outside, but it smells nice. So, let's begin the presentation. So, here's, today's agenda. First let's go over. What is an architecture? It's a very loaded word. It means a number of things, for developers of any level, it's an important term to have down pat. W hat are the common architectures? There's going to be a lot of abbreviations that you hear today. How does SwiftUI change the playing field? SwiftUI is two-way data-binding makes the previous architecture somewhat moot in certain cases. And I'm here to talk about that. And comparing the architectures and pretty much injecting into this, from my professional opinion what the most logical architecture to use with SwiftUI is. If there is time, I have some bonus slides on networking and testing using the various architectures.
Jason Flax: But if there is not, I will defer to the Q&A where you all get to ask a bunch of questions that Shane had enumerated before. So, let us begin. What is an architecture? x86, PowerPC, ARM. No, it's not, this is not, we're not talking about hardware architecture here. Architecture is short for an architectural pattern. In my opinion, hardware is probably too strong of a word or architecture is too strong of a word. It's just a term to better contextualize how data is displayed and consumed it really helps you organize your code. In certain cases, it enhances testability. In certain cases, it actually makes you have to test more code. Basically the patterns provide guidelines and a unified or United vocabulary to better organize the software application.
Jason Flax: If you just threw all of your code onto a view, that would be a giant mess of spaghetti code. And if you had a team of 20 people all working on that, it would be fairly measurable and a minimum highly disorganized. The images here, just MVC, MVVM, Viper, MBI. These are the main ones I'm going to talk about today. There are a number of architectures I won't really be touching on. I think the notable missing one from this talk will be CLEAN architecture, which I know is becoming somewhat big but I can address that later when we talk or in the Q&A.
Jason Flax: Let's go over some of those common architectures. So, from the horse's mouth, the horse here being Apple the structure of UIKit apps is based on the Model-View-Controller design pattern, wherein objects are divided by their purpose. Model objects manage the app's data and business logic. View objects provide the visual representation of your data. Controller objects acts as a bridge between your model and view objects, moving data between them at appropriate times.
Jason Flax: Going over this, the user uses the controller by interacting with the view, the view talks to the controller, generally controllers are going to be one-to-one with the view, the controller then manipulates the data model, which in this case generally speaking would actually just be your data structure/the data access layer, which could be core data or Realm. The model then updates the view through the controller is displayed on the view the user sees it, interacts with it goes in a big circle. This is a change from the original MVC model. I know there are a few people in this, attending right now that could definitely go into a lot more history than I can. But the original intent was basically all the data and logic was in the model.
Jason Flax: The controller was just for capturing user input and passing it to the model. And the communication was strictly user to controller, to model, to view. With no data flowing the other way this was like the OG unidirectional data flow. But over time as the MVC model evolved controllers got heavier and heavier and heavier. And so what you ended up with is MVC evolving into these other frameworks, such as MVVM. MVVM, Viper, CLEAN. They didn't come about out of nowhere. People started having issues with MVC, their apps didn't scale well, their code didn't scale well. And so what came about from that was new architectures or architectural design patterns.
Jason Flax: Let's go over Model-View-ViewModel. It's a bit of a mouthful. So, in MVVM the business logic is abstracting to an object called a ViewModel. The ViewModel is in charge of providing data to the view and updating the view when data changes, traditionally this is a good way to separate business logic from the view controller and offer a much cleaner way to test your code. So, generally here, the ViewModel is going to be one-to-one with the model as opposed to the view and what the ViewModel ends up being is this layer of business logic and presentation logic. And that's an important distinction from what the controller previously did as the controller was more associated with the view and less so the model. So, what you end up with, and this will be the pattern as I go through each of the architectures, you're going to end up with these smaller and smaller pieces of bite-sized code. So, in this case, maybe you have more models than views. So, MVVM makes more sense.
Jason Flax: So ViewModel is responsible for persistence, networking and business logic. ViewModel is going to be your data access layer, which is awkward when it comes to something like Realm, since Realm is the data access layer. I will dig more into that. But with SwiftUI you end up with a few extra bits that don't really make much sense anymore. This is just a quick diagram showing the data flow with MVVM. The ViewModel binds the view, the user inputs commands or intent or actions or whatever we want to say. The commands go through to the ViewModel, which effectively filters and calculates what needs to be updated on the model, it then reads back from the model updates the view does that in sort of a circular pattern.
Jason Flax: Let's go over Viper. So, Viper makes the pieces even smaller. It is a design pattern used to separate logic for each specific module of your app. So, the view is your SwiftUI, A View owns a Presenter and a Router. The Interactive, that is where your business logic for your module lives, the interactor talks to your entity and other services such as networking. I'll get back to this in a second. The presenter owns the interactor and is in charge of delivering updates to the view when there is new data to display or an event is triggered. So, in this case, the breakdown, if we're associating concepts here, the presenter is associated with the view. It's closer to your view controller and the interactor is more associated with the model. So, it's closer to your ViewModel. So, now we're like mixing all these concepts together, but breaking things and separating things, into smaller parts.
Jason Flax: In this case, the data flow is going to be a bit different. Your View is going to interact with the Presenter, which interacts with the Interactor, which interacts with the Entity. So you end up with this sort of onion that you're slowly peeling back. The Entity is your data model. I'm not entirely sure why they didn't call it model my guess is that Viper sounds a lot better than [inaudibile 00:06:46] doesn't really work. The router handles the creation of a View for a particular destination. This is a weird one. I'll touch on it a couple of times in the talk.
Jason Flax: Routers made more sense when the view flow was executed by storyboards and segues and nibs and all that kind of thing. Now it's SwiftUI because it's all programmatic, routers don't really make as much sense. That said maybe in a complex enough application, I could be convinced that a router might elucidate the flow of use, but at the moment I haven't seen anything yet. But that is what the router is meant to do anyway, this is a brief diagram on how Viper works, sorry. So, again the View owns and sends actions to the Presenter, the Presenter owns and asks for updates and sends updates to the Interactor and the Interactor actually manipulates the data, it edits the entity, it contains the data access layer, it'll save things and load things and will update things.
Jason Flax: And then the Interactor will notify the Presenter, which then notifies them View. As you can see this ... For anybody that's actually used SwiftUI, this is immediately not going to make much sense considering the way that you actually buying data to Views. MVI, this is kind of our end destination, this is where we want to end up at the end of our journey. MVI is fairly simple, to be honest, it's closer to an ideal, it's closer to a concept than an architecture to be honest. You have user, user has intent that intent changes the model, that model changes the view user sees that and they can keep acting on it. This has not really been possible previously UIKit was fairly complex, apps grow in complexity. Having such a simple thing would not have been enough in previous frameworks, especially uni-directional ones where a circular pattern like this doesn't really make sense.
Jason Flax: But now it's SwiftUI there's so much abstracted way with us, especially with Realm, especially with live objects, especially with property rappers that update the view automatically under the hood that update your Realm objects automatically under the hood. We can finally achieve this, which is what I'm going to be getting out in the stock. So, let's go over some of those common concepts. Throwing around terms like View and Presenter and Model is really easy to do if you're familiar, but just in case anybody isn't. The View is what the user sees and interacts with all architectural patterns have a view. It is the monitor. It is your phone. It is your whatever. It is the thing that the user touches that the user plays with.
Jason Flax: The User, the person behind the screen, the user actions can be defined as intent or interactions, actions trigger view updates which can trigger business logic. And I do feel the need to explicitly say that one thing that is also missing from this presentation not all actions, not all intent will be user-driven there are things that can be triggered by timers things that can be triggered by network requests things that don't necessarily line up perfectly with the model. That said, I felt comfortable leaving out of the top because it actually doesn't affect your code that much, at least if you're using MVI.
Jason Flax: The model. So, this term gets a bit complicated basically it's the central components of the pattern. It's the application status structure, independent of the user interface it manages the data, logic and rules of the application. The reason that this gets a bit wonky when describing it is that oftentimes people just speak about the model as if it was the data structures themselves as if it was my struct who with fields, bars, whatever. That is the model. It's also an object. It's also potentially an instance of an object. So, hopefully over this talk, I can better elaborate on what the model actually is.
Jason Flax: The Presenter. So, this is the presenter of the ViewModel, whatever you want to call it. It calculates and projects what data needs to actually be displayed on the view. As more often referred to as the ViewModel, the presenter. Again, frameworks with two-way data-binding obviate the need for this. It is awkward to go to a presenter when you don't necessarily need to. So, let's get the meat of this, how does SwiftUI actually change the playing field? For starters there is no controller. It does not really make sense to have a controller. It was very much a UIKit concept ironically or coincidentally by eliminating the controller this graphic actually ends up looking a lot like MVI. The user manipulates the model through intent, the model updates, the view, the user sees the view goes around in a big circle. I will touch on that a bit more later. But MVC really doesn't make as much sense anymore if you consider it as the new school MVC.
Jason Flax: MVVM, added a few nice screen arrows here. The new model doesn't really make sense anymore. Again, this is the presentation layer. When you can bind directly to the data, all you're doing by creating a ViewModel with SwiftUI in a two-way data-binding framework is shifting responsibility in a way that creates more code to test and doesn't do much else especially these days where you can just throw that business logic on the model. If you were doing traditional MVVM with SwiftUI, the green arrows would be going from, View to ViewModel and you could create the same relationship. It's just extra code it's boiler plate. Viper lot of confusion here. Again not really sure that the router makes a lot of sense. I can be convinced otherwise. Presentation view, the presenter doesn't really again makes sense it's basically your ViewModel. Interactor also doesn't make sense. It is again, because the view is directly interacting with the model itself or the entity. This piece is again, kind of like, eh, what are you doing here?
Jason Flax: There's also an element here that as you keep building these blocks with Viper and it's both a strength and weakness of Viper. So, the cool thing about it you end up with all these cool little pieces to test with, but if the 10,000 line controller is spaghetti code. 10,000 lines of Viper code is ravioli code. Like these little pieces end up being overwhelming in themselves and a lot of them do nothing but control a lot at the same time. We'll get more into that when I show the actual code itself. And here's our golden MVI nothing changes. This is the beauty of it. This is the simplicity of it. User interacts, changes the model changes the view ad infinitum.
Jason Flax: Now, let's actually compare these with code. So, the app that we will be looking at today, Apple came out with a Scrumdinger app to show offs with UI. It is basically an app that lets you create scrums. If you're not familiar with the concept of scrum it's a meeting, it's a stand-up where people briefly chat and update and so on and so forth. And I could go into more detail, but that's not what this talk is about. So, we took their app and we added Realm to it and we also then basically wrote it three different times, one in Viper, one in MVVM and one in MVI. This will allow us to show you what works and what doesn't work. Obviously it's going to be slightly biased towards MVI. And of course I do feel the need to disclaim that this is a simple application. So, there's definitely going to be questions of like, "How does this scale? How does MVI actually scale?" I can address those if they are asked, if not, it should become pretty clear why I'm pushing MVI as the go-to thing for SwiftUI plus Realm plus the Realm Property Wrappers.
Jason Flax: Let's compare the models. So, in Viper, this is your entity. So, it contains the scrums so the DailyScrum is going to be our like core data structure here. It's going to contain an ID. It's going to contain a title, it's going to contain attendees and things like that. But the main thing I'm trying to show with this slide is that the entity loads from the Realm, it loads the DailyScrum, which for those that have used Realm, you already know this is a bit awkward because everything with Realm is live. Those DailyScrum, that objects Realm.objects(DailyScrum.self).map. So, if you were to just save out those objects, those results are always updating, those results read and write, directly from the persistent store. So, loading from the database is already an awkward step.
Jason Flax: Otherwise, you can push new scrums. You can update scrums. Again, what this is doing is creating a data access layer for a database that is already the data access layer. Either way this is the idea of Viper. You are creating these abstractions to better organize how things are updated, pushed, loaded, et cetera. MVVM the model is still a more classically the data structure itself. I will show the actual ViewModel in another slide. This should look a bit more similar to probably what you'd be used to. Actually there's probably a mistake the color shouldn't be there because that should be in the ViewModel. But for the most part, these are your properties.
Jason Flax: The big difference here between MVI will be the fact that again you're creating this access layer around the Realm where you're going to actually pass in the ViewModel to update the scrum itself and then right to the Realm. It's even possible that depending on your interpretation of MVVM, which again is something I should have actually said earlier in the talk, a lot of these architectures end up being up for interpretation. There is a level of subjectivity and when you're on a team of 20 people trying to all architect around the same concepts, you might end up with some wishy-washy ViewModels and models and things like that.
Jason Flax: MVI, this is your model that is the Realm database, and I'm not actually being facetious here. If you consider Realm to be your data access layer, to be your persistent storage, to be the thing that actually syncs data, it is. It holds all your data, It maintains all of the state, which is kind of what the model is supposed to do to. It maintain state, it maintains the entire, flow and state of your application. That is what Realm can be if you use it as it's intended to be used. Let's go over what the View actually look like depending on your architecture. Spoilers, they look very similar. It's what's happening under the hood that actually really changes the game. So, in this case you have these ScrumsView.
Jason Flax: The object that you have on the View, you'll notice this does not, even though this app uses Realm is not using the Realm property wrappers because you are presenting the Presenter kind of makes sense, I suppose. You're going to show the scrums from the presenter and you're going to pass around that presenter around this view, to be able to interact with the actual underlying model, which is the DailyScrum class. You'll also notice at the bottom, which is I suppose a feature of Viper, each view has a presenter and potentially each model has an interactor.
Jason Flax: For the EditView. So, I have the scrum app, I want to edit the scrum I have, I want to change the title or the color of it or something like that. I want to add attendees. For Viper, you have to pass in a new presenter, you have to pass in a new interactor and these are going to be these bite-sized pieces that again interact with the View or model, depending on which thing you're talking about. How am I doing on time? Cool. So, this is the actual like EditView now that I'm talking about. So, the EditView has that presenter, that presenter is going to basically give the view all of the data. So, lengthInminutes title, color, attendees, things like that. They're all coming off the presenter. You can see over here where I'm circling, you would save off the presenter as well. So, when you're done editing this view, you save on the presenter, that presenter is actually going to then speak to the interactor and that interactor is going to interact with their database and actually say about that data.
Jason Flax: Again, the reason that this is a bit awkward when using Realm and SwiftUI at least is that because you have live objects with Realm, having intermediary layers is unnecessary abstraction. So, this is MVVM and now we actually have the video of the View as well on the right side. So, instead of a Presenter, you have a ViewModel right now you're seeing all the terms come together. You're going to read the ViewModels off of the ViewModel for each view. So, for the detailed view, you're going to pass in the detail ViewModel for the EditView, you're going to pass in the EditViewModel and the set ViewModel is going to take a scrum and it's going to read and write the data into that scrum.
Jason Flax: This is or MBI now. So, MBI is going to look a little different. The view code is slightly larger but there are no obstructions beyond this. So, in this case, you have your own property wrap, you have observed results. This is going to be all of the DailyScrum in your round database. It is going to live update. You are not going to know that it's updating, but it will notify the view that it's updating. So, the DailyScrum was added, say, you have Realm sync, a DailyScrum is added from somebody else's phone, you just update. There is no other code you have to write for that. Below that you have a StateRealmObject, which is new scrum data. So, in this case, this is a special case for forms, which is a very common use case, that scrum data is going to be passed into the EditView and it's going to be operated on directly.
Jason Flax: So the main added code here, or the main difference in code is this bit right here, where we actually add the scrum data to the observed results. So, somebody, following MVVM or Viper religiously might say, that's terrible. "Why are you doing business logic in a view like that? And why would that happen?" This is a direct result of user action. A user hits the done button or the add button. This needs to happen afterwards technically, if you really wanted to, you could extract this out to the model itself. You could put this on in instance of new scrum data and have it write itself to the Realm that is totally valid. I've seen people do that with, MBI, SwiftUI and Realm. In this case, it's simple enough where those layers of abstraction don't actually add anything beneficial.
Jason Flax: And for testing, this would be, you'd want to test this from a UI test anyway. And the reason for that is that we test that the scrum data is added to the Realm we being Realm. I suppose there's a level of trust you have to have with Realm here that we actually are testing our code, we promise that we are. But that's the idea is that all of this appending data, all of this adding to the database, the data access layer, that's tested by us. You don't have to worry about that. So, yeah. Why is this view larger than the MVVM view? Because the interactive logic has been shifted to the ViewModel in MVVM, and there's no extra logic here, it's all there. But for MVVM again, it's all been pushed back, the responsibility has been shifted slightly. MVI this is actually what the EditView would look like. There's no obstructions here. You have your StateRealmObject, which is the DailyScrum that's been passed in from the previous view. And you bind the title directly to that.
Jason Flax: So if you look at the right side video here, as I changed the Cocoa team to Swift team Scrum so mouthful that is updating to the Realm that is persisting and if you were using Realm sync, that would be syncing as well. But there is no other logic here that is just handled. Hopefully at this point you would be asking yourself why add the extra logic. I can't give you a good reason, which is the whole point of this talk. So, let's go over the ViewModel and Persistence or dig in a bit deeper. So, this is our actual Realm Object. This is the basic object that we have. It's the POJO, the POSO whatever you want to call it. It is your plain old Swift object.
Jason Flax: In this case, it is also a Realm Object has an ID. That ID would largely be for syncing if you had say, if you weren't using Realm Sync and you just had a REST API, it would be your way of identifying the Scrums. It has a title, which is the name of it of course, a list of attendees, which in this case for this simple use case, it's just strings it's names it's whatever that would be length of the scrum in minutes and the color components, which depending on which thing you're using is actually pretty cool. And this is something that I probably won't have time to fully dig into, but you can use Realm to manage view state. You can use Realm to manage the app state if say you're scrolling in a view and you're at a certain point and then you present another view over that maybe it's a model or something, and the phone dies, that sucks.
Jason Flax: You can open up the app when the phone turns back on, if they've charged it of course, and you can bring them back to that exact state, if it's persistent in the Realm. In the case of color components, the cool thing there is that you can have a computer variable, which I'll show after that will display directly to the view as a color. And with that binding, the view can also then change that color, the model can break it down into its components and then store that in the Realm. Let's actually skip the presenter in that case, because we were actually on the EditView, which I think is the more interesting view to talk about. So, this is the edit presenter for Viper.
Jason Flax: This is your ViewModel, this is your presenter. And as you can see here, it owns the Interactor and it's going to modify each of these fields as they're modified. It's going to fetch them. It's going to modify them. It's going to send updates to the view because it can't take advantage of Realms update. It can't take advantage of Realms observe or the Property wrappers or anything like that because you are creating this, separation of layers. In here with colors it's going to grab everything. And when you actually add new attendees, it's going to have to do that as well. So, as you can see, it just breaks everything down.
Jason Flax: And this is the Interactor that's actually now going to talk to the model. This is where your business logic is. This is where you could validate to make sure that say the title's not empty, that the attendees are not empty, that the length of time is not negative or something like that. And this is also where you'd save it. This would be the router, which again I didn't really know where to put this. It doesn't fit in with any other architecture but this is how you would present views with Viper
Jason Flax: And for anybody that's used SwiftUI you might be able to see that this is a bit odd. So, this would be your top level ViewModel for MVVM. In this case, you actually can somewhat take advantage of Realm. If you wanted to. Again, it depends on how by the book you are approaching the architecture as, so you have all your scrums there, you have what is, and isn't presented. You have all your ViewModels there as well, which are probably going to be derived from the result of scrums. And it's going to manage the Realm. It's going to own the Realm. It's going to own a network service potentially. You're going to add scrums through here. You're going to fetch scrums through here. It controls everything. It is the layer that blocks the data layer. It is the data access layer.
Jason Flax: And this is going to be your ViewModel for the actual DailyScrum. This is the presentation layer. This is where you're seeing. So you get the scrum title that you change the scrum title, and you get the scrum within minutes you change the scrum length in minutes. You validate it from here, you can add it from here. You can modify it from here. It also depends on the view. But to avoid repeating myself and this would be the EditView with the ViewModel. So, instead of having the Realm object here, as you saw with MBI, you'd have the ViewModel. The two-way data-binding is actually going to change the model. And then at the end you can update. So, things don't need to be live necessarily. And again the weird thing here is that with Realms live objects, why would you want to use the ViewModel when you have two-way data-binding?
Jason Flax: And just to ... My laptop fan just got very loud. This is the path to persistence with MVVM as well. So, user intent, user interaction they modify the view in whatever way they do it. it goes through the presenter, which is the DailyScrum ViewModel. This is specifically coming from the EditView. It goes to the presenter. It changes the DailyScrum model, which then interacts and persists to the Realm. Given anybody that's used Realm again to repeat myself, this is a strange way to use Realm considering what Realm is supposed to be as your persistent storage for live objects.
Jason Flax: MVI, what is your presentation layer? There's no VM here. There's no extra letters in here. So, what do we actually do? In MVI, the presentation layer is an instance of your data model. It is an instance of these simple structures. So in this case, this is the actual DailyScrum model. You can see on here, the color thing that I was talking about before. This color variable is going to bind directly to the view and when the view updates, it will update the model. It will persist to the Realm. It will sync to MongoDB Realm. It will then get the color back showed in the view, et cetera. And for business logic, that's going to be on the instance. This could be an extension. It could be in an extension. It could be in a different file. There's ways to organize that obviate the need for these previously needed abstractions in UIKit.
Jason Flax: So, this is an actual implementation of it, which I showed earlier. You have your StateRealmObject, which is the auto updating magic property wrapper in SwiftUI. You have your DailyScrum model and instance has been passed in here. So, when we actually write the title down, type the title, I suppose. Because it's on the phone, it is going to update sync persist, et cetera. MVI is a much shorter path to persistence because we are binding directly to the view. User makes an action, action modifies the view, view modifies the actual state. You modifies the Realm, modifies the DailyScrum syncs et cetera.
Jason Flax: Why MVI is NOT scary, not as an all capital letters because I'm super serious guys. So, MVI is lightweight. It's nearly a concept as opposed to a by the book architecture. There are standards and practices. You should definitely follow your business logic should be on the actual instance of the data model. The two-way data-bindings should be happening on the view itself. There's some wiggle room, but not really, but the implication is that the View is entirely data-driven. It has zero state of its own, bar a few dangling exceptions, like things being presented, like views being presented or scroll position or things like that.
Jason Flax: And all UI change has come from changes in the model which again, leveraging Realm, the model auto-updates and auto-notifies you anyway. So, that is all done for you. SwiftUI though imperfect does come very close to this ideal. View state can even be stored and persisted within the guidelines of the architecture to perfectly restore user state in the application, which ties back to the case I gave of somebody's phone dying and wanting to reopen right to the exact page that they were in the app. So, when considering the differences in SwiftUI's two way databinding versus UIKit's unidirectional data flow, we can rethink certain core concepts of at least MVVM and to an extent Viper.
Jason Flax: And this is where rethinking the acronyms or abbreviations comes into play a bit. It's a light spin, but I think for those who actually, let's say, you're on your team of 20 iOS developers and you go to the lead engineer and you're like, "I really think we should start doing MVI." And they're like, "Well, we've been doing MVVM for the past five years. So, you can take a walk." In this case, just rephrase MVVM. Rephrase Viper. In this case, your model becomes the round database. It is where you persist state. It is the data access layer. The View is still the View. That one doesn't change. The ViewModel, again, just becomes an instance of your Realm object.
Jason Flax: You just don't need the old school ViewModel anymore. The business logic goes on that. The transformation goes on that. It is honestly a light shift in responsibility, but it prevents having to test so much extra boilerplate code. If the goal of MVVM was to make things easier to test in previous iterations of iOS development, it no longer applies here because now you're actually just adding extra code. Viper concepts can be similarly rethought. Again, your View is your View, your presenter and interactor or the ViewModel. Your entity is the model and your router is enigma to me. So, I'll leave that one to the Viper doves out there to figure out. It looks like we have enough time for the extra slides that I have here before the Q &A.
Jason Flax: So, just a bit of networking code, this is really basic. It's not very good code either. So, in this case, we're just going to fetch the scrums from a third party service. So, we're not using Realm sync in this case. We have some third party service or our own service or whatever that we call in. And if we want those to show on the View, we're going to have to notify the View. We're going to want the cache those maybe. So, we're going to add those to the Realm. If they actually do have IDs, we want to make sure that our update policy does not overwrite the same scrums or anything like that for updating. And this is Viper, by the way. For updating similarly, we're going to pass the scrum to the interactor. That scrum is going to get sent up to the server. We're going to make sure that that scrum is then added to the Realm, depends on what we're updating.
Jason Flax: If we've updated it properly and using Realm as Realm is intended to be used, you should not have to re-add it to the Realm. But if you are following Viper by the book, you need to go through all the steps of reloading your model, saving this appropriately and updating the View, which again is a lot of extra work. Not to mention here as well, that this does not account for anything like conflicts or things that would happen in a real-world scenario, but I will get to that in a later slide. So, for MVVM in this case, the networking is likely going to be on the ViewModel and go through again, some kind of service. It's very similar to Viper, except that it's on the ViewModel we're going to fetch.
Jason Flax: We're going to add to the Realm of cache, cache and layer. And because we're not using the Realm property wrappers on the View, we're using the ViewModel, we have to update the View manually, which is the objectWillChange.send. So, for MVI, it's similar, but again, slightly different because the Realms are on the View this time, the main difference here is that we don't have to update anything. That results from before the observed results. That's going to automatically update the View. And for the update case, you shouldn't really have to do anything, which is the big difference between the other two architectures because you're using live objects, everything should just be live.
Jason Flax: And because in MVI, the business logic is going to be on the data models themselves or the instances of the Realm objects themselves. These methods are going to be on that, you update using yourself which is here. And the cool thing, if you're using MongoDB Realm Sync and you're thinking about networking, you don't have to do anything. Again, not being facetious, that's handled for you. If you're using your persistence layer and thinking about sync, when you actually open up the Realm, those scrums are going to be pulled down for you, and they're going to hydrate the Realm.
Jason Flax: If somebody on their phone updates one of the existing scrums, that's going to be automatically there for you. It is going to appear on your View live without you having to edit any extra code, any extra networking or whatever. Similar, removal. And of course, Realm sync also handles things like conflicts, where if I'm updating the scrum at the same time as somebody else, the algorithm on the backend will figure out how to handle that conflict for you. And your Realm, your persistence layer, your instances of your data models as well, which is another cool feature from because remember that they're live, they will be up-to-date.
Jason Flax: They will sync to the Views, which will then have the most up-to-date information without you adding any extra code. I would love to go into this more. So, for my next talk, I think the thing I want to do, and of course I'd like to hear from everybody if they'd be interested, but the thing I want to do is to show a more robust, mature, fully fledged application using MVI MongoDB Realm sync, SwiftUI Realm and the property wrappers, which we can talk about more in the Q&A, but that's my goal. I don't know when the talk will be, but hopefully sooner than later. And then finally, the last bit of slides here. Actually, testing your models. So, for MVVM you actually have to test the ViewModels. You're going to test that things are writing to the database and reading from database appropriately.
Jason Flax: You're testing that the business logic validates correctly. You're testing that it calculates View data correctly. You're testing out all of these calculations that you don't necessarily have to test out with other architectures. Viper, it's going to be the same thing. You're just literally swapping out the ViewModel for the interactor and presenter. But for MVI, colors are a little messed up there. You're really just going to be testing the business logic on your models. You're going to create instances of those Realm objects and make sure that the business logic checks out. For all of these, I would also highly recommend that you write UI tests. Those are very important parts of testing UI applications. So, please write those as well. And that's it. Thank you, everyone. That is all for the presentation. And I would love to throw this back to Ian and Shane, so that we can start our Q&A.
Shane McAllister: Excellent. Thank you. Thank you, Jason. That was great. I learned a lot in that as well, too. So, do appreciate that. I was watching the comments on the side and thank you for the likes of Jacob and Sebastian and Ian and Richard and Simon who've raised some questions. There's a couple that might come in there. But above all, they've been answered by Lee and also Alexander Stigsen. Who, for those of you who don't know, and he'll kill me for saying, is the founder of Realm and he's on the chat. So, if you question, drop it in there. He's going to kill me now. I'm dead. So, I think for anybody, as I said at the beginning, we can open and turn on your camera and microphone if you want to ask a question directly.
Shane McAllister: There's no problem if you don't want to come on camera well you can throw it into the chat and I'll present it to essentially Jason and Ian and we'll discuss it. So, I think while we're seeing, if anybody comes in there, and for this scrum dinger example, Jason, are we going to put our Realm version up on a repo somewhere that people can play around with?
Jason Flax: Yes, we will. It is not going to be available today, unfortunately. But we are going to do that in the next, hopefully few days. So, I will I guess we'll figure out a way to send out a link to when that time comes.
Shane McAllister: Okay. So, we've a question from Jacob. "And what thoughts do you have on using MVI for more mixed scenarios, for example, an app or some Views operate on the database while others use something like a RIA service?"
Jason Flax: Where is that question, by the way, [crosstalk 00:38:31].
Shane McAllister: On the chat title, and there's just a period in the chat there it'll give you some heads up. Most of the others were answered by Alexander and Lee, which is great. Really appreciate that. But so looking at the bottom of the chat there, Jason, if you want to see them come through.
Jason Flax: I see, Jacob. Yeah. So, I hope I was able to touch on that a bit at the end. For Views that need to talk to some network service, I would recommend that that logic again, no different than MVVM or Viper, that logic, which I would consider business logic, even though it's talking to RIA service, it just goes back on the instance of the object itself. In certain cases, I think let's say you're fetching all of the daily scrums from the server, I would make that a static method on the instance of the data object, which is mainly for organizational purposes, to be honest. But I don't think that it needs to be specially considered beyond that. I'm sure in extremely complex cases, more questions could be asked, but I would probably need to see a more complex case to be able to-
Ian Ward: I think one of the themes while you were presenting with the different architecture patterns, is that a lot of the argument here is that we are eliminating boilerplate code. We're eliminating a lot of the code that a developer would normally need to write in order to implement MVVM or there was a talk of MVC as Massive View Controller. And some of the questions around MVI were, "Do we have the risk of also maybe inflating the model as well here?" Some of that boilerplate code now go into the model. How would you talk to that a little bit of putting extra code into the model now to handle some of this?
Jason Flax: As in like how to avoid this massive inflation of your model [crosstalk 00:40:33]?
Ian Ward: Yeah. Exactly. Are we just moving the problem around or does some of this eliminate some of that boilerplate?
Jason Flax: To be honest, each one of these [crosstalk 00:40:45].
Ian Ward: That's fair. I guess that's why it's a contentious issue. You have your opinions and at some point it's, where do you want to put the code?
Jason Flax: Right. Which is why, there is no best solution and there is no best answer to your question either. The reason that I'm positing MVI here is not necessarily about code organization, which is always going to be a problem and it's going to be unique to somebody's application. If you have a crazy amount of business logic on one of your Realm objects, you probably need to break up that Realm object. That would be my first thought. It might not be true for each case. I've seen applications where people have 40 different properties on their Realm object and a ton of logic associated with it. I personally would prefer to see that broken down a bit more.
Jason Flax: You can then play devil's advocate and say, "Well, okay," then you end up with the Ravioli Code that you were talking about from before. So it's all, it's this balancing act. The main reason I'm positing MVI as the go-to architecture is less about code organization and more about avoiding unnecessarily boilerplate and having to frankly test more than.
Ian Ward: Right.That's a fair answer. And a couple of questions that are coming in here. There's one question at the beginning asking about the block pattern, which watch out sounds like we have a flutter developer in here. But the block pattern is very much about event streams and passing events back and forth, which although we have the property wrappers, we've done a lot of the work under the hood. And then there was another question on combined. So, maybe you could talk a little bit about our combined support and some of the work that we've done with property wrappers to integrate with that.
Jason Flax: Sure. So, we've basically added extensions to each of our observable types which in the case of Realm is going to be objects, lists, backlinks, results which is basically a View of the table that the object is stored on, which can be queried as well. And then by effect through objects, you can also observe individual properties. We have added support to combine. So, you can do that through the flow of combine, to get those nice chains of observations, to be able to map data how you want it to sync it at the end and all that kind of thing. Under the hood of our property wrappers are hooking that observation logic into SwiftUI.
Jason Flax: Those property wrappers themselves have information on that, so that when a change happens, it notifies the View. To be honest, some of that is not through combined, but it's just through standard observation. But I think the end mechanism where we actually tell the View, this thing needs to update that is through, I guess, one of the added combined features, which is the publisher for object changes. We notified the View, "Hey, this thing is updated." So, yeah, there's full combine support for Realm, is the short answer as well.
Ian Ward: Perfect.
Shane McAllister: Cool. There was a question hiding away in the Q&A section as well too. "Does at state Realm object sends Realm sync requests for each key stroke?"
Jason Flax: It would. But surprisingly enough, that is actually not as heavy of an action as you might think. We've had a lot of debate about this as well, because that is one of the first questions asked when people see the data being bound to a text field. It's really not that heavy. If you are worried about it or maybe this is just some application that you want to work in the Tundras of Antarctica, and maybe you don't want to have to worry about things like network connection or something, I would consider using a draft object, something that is not being persistent to the Realm. And then at the end, when you are ready to persist that you can persistent it. Classically, that would have been the ViewModel, but now you can just use an instance of a non-persistent Realm object, a non [crosstalk 00:44:51].
Ian Ward: Yeah. That was another question as well. I believe Simon, you had a question regarding draft objects and having ... And so when you say draft objects, you're saying a copy of the Realm object in memory, is that correct? Or maybe you can go into that a little bit.
Jason Flax: It could be a copy. That would be the way to handle an existing object that you want to modify, if you don't want to set it up on every keystroke for form Views in this case, let's say it's a form. Where it doesn't exist in the Realm, you can just do an on manage type and to answer Simon's second query there. Yeah, it could also be managed by a local Realm that is also perfectly valid, and that is another approach. And if I recall Simon, were you working on the workout app with that?
Ian Ward: I believe he was.
Jason Flax: I don't know. Yeah. Yeah. I played around with that. That is a good app example for having a lot of forms where maybe you don't want to persist on every keystroke. Maybe you even want something like specifically, and I believe this might've even been the advice that I gave you on the forums. Yes, store a draft object in a local Realm. It could be the exact same object. It could be a different model that is just called, let's say, you want to save your workout and it has sets and reps and whatever. You might have a workout object stored in the sync Realm, and then you might have a workout draft object stored in a local Realm, and you can handle it that way as well.
Shane McAllister: Great. Does anybody want to come on screen with us, turn on the camera, turn on the mic, join us? If you do, just ping in the chat, I'll jump in. I'll turn that right on for you. Richard had a question further up and it was more advice, more so than a question per se, "Jason, nice to show some examples of how you would blend MVI with wrapped View controllers." He's saying that rewrites are iterative and involve hybrid systems was the other point he made.
Jason Flax: Right. Yeah. That would be a great concept for another talks because yeah, you're totally right. It's really easy for me to come in with a cricket bat or whatever, and just knock everything down and say, "Use MVI." But in reality of course, yeah, you want to incrementally migrate to something you never want to do ever. Well, not never, but most of the time you don't want to do a total rewrite.
Ian Ward: Yeah, a total rewrite would be a sticky wicket, I think. So, for cricket. So, we have another question here on Realm's auto-sync. And the question from Sebastian is, "Can we force trigger from an API sync?" And actually I can answer this one. So, yes, you can. There is a suspend and resume method for Realm sync. So, if you really want to be prescriptive about when Realm syncs and doesn't sync, you can control that in your code.
Jason Flax: Perfect.
Shane McAllister: And asks, "Is there any learning path available to get started with Realm?" Well, we've got a few. Obviously our docs is a good place to start, and if you go look in there, but the other thing too is come on who, and this is the plug, developer.mongodb.com. And from there, you can get to our developer hub for articles. You can get into our forums to ask the questions of the engineers who are on here and indeed our wider community as well too. But we're also very active where our developers are. So, in GitHub and Stack Overflow, et cetera, as well too, there's comments and questions whizzing around there. Jason, is there anywhere else to go and grab information on getting started with Realm?
Shane McAllister: Yeah. Obviously this is the place to go as well too. I know we're kind of, we went in at a high level and a lot of this here and maybe it's not obviously the beginner stuff, but we intend to run these as often as we can. Certainly once or twice a month going forward, resources permitting and time permitting for everybody too. So, as Ian said, I think at the beginning, tell us what you want to hear in meetups like this as well too because we want to engage with our community, understand where you're at and help you resolve your problems with Realm as much as possible.
Ian Ward: Absolutely
Shane McAllister: Ian has another one in here, Ian. Thank you, Ian. "And how to move a local Realm into sync? Just copy the items manually from one to the other or is there a switch you can throw to make the local one a synced one?"
Ian Ward: Yeah.
Jason Flax: [crosstalk 00:49:49]. So, we do get this feature request. It is something that is on my list, like by list of product backlog. Definitely something I want to add and we just need to put a product description together, another thing on my backlog. But yes, right now what you would do is to open the local Realm, iterate through all the objects, copy them over into a synced Realm. The issue here is that a synced Realm has to match the history of the MondoDB Realm sync server on the side. So, the histories have to match and the local Realm doesn't have that history. So, it breaks the semantics of conflict resolution. In the future, we would like to give a convenience API to do this very simply for the user. And so hopefully we can solve that use case for you.
Shane McAllister: Good. Well, Ian has responded to say, "That makes sense." And indeed it does, as always. Something else for your task list then. So, yeah, definitely.
Ian Ward: Absolutely.
Shane McAllister: I'm trying to scroll back through here to see, did we miss anybody. If we did miss anybody, do to let me know. I noticed a comment further up from [Anov 00:51:01], which was great to see, which is, "These sessions turn out to be the best use of my time." And that's what we're looking for, that validity from our community, that this is worth the time. Jason puts a ton of effort into getting this prepared as does Ian and pulling it all together. Those examples don't write themselves. And indeed the wider team, the Coca team with Jason as well had put effort into putting this together. So, it's great to see that these are very beneficial for our community. So, unless, is there anything else, any other questions? I suppose throwing it back out to you, Jason, what's next? What's on the roadmap? What's keeping you busy at the moment? Ian, what are we planning later on? You're not going to say you can't tell, right?
Ian Ward: Yeah. For iOS specifically, I think maybe Jason, we were talking about group results. I know we had a scope the other day to do that. We're also talking about path filtering. These are developer improvements for some of the APIs that we have that are very iOS-specific. So, I don't know, Jason, if you want to talk to a couple of those things that would be great.
Jason Flax: Sure. Yeah. And I'll talk about some of stuff that hopefully we'll get to next quarter as well. So, group results is something we actually have to figure out and ironically actually ties more to UIKit and basically how to better display Realm data on table Views. But we are still figuring out what that looks like. Key path filtering is nice. It just gives you granual observation for the properties that you do actually want to observe and listen to on objects. Some of the other things that we've begun prototyping, and I don't think it's ... I can't promise any dates. Also, by the way, Realm is open source. So, all of this stuff that we're talking about, go on our branches, you can see our poll requests. So, some of the stuff that we're prototyping right now Async rights, which is a pretty common use case we're writing data to Realm asynchronously.
Jason Flax: We're toying with that. We're toying with another property wrapper called auto-open, which will hopefully simplify some of the logic around MongoDB Realm locking in and async opening the Realm. Basically the goal of that project is so that let's say your downloading a synced Realm with a ton of data in it as opposed to having to manually open the Realm, notify the View that it's done, et cetera, you'll again, just use a property wrapper that when the Realm is done downloading, it will notify the View that that's occurred. We're also talking about updating our query syntax. That one I'm particularly excited about. Again, no dates promised. But it will basically be as opposed to having to use NS predicate to query on your Realm objects, you would be able to instead use a type safe key path based query syntax, closer to what Swift natively uses.
Ian Ward: Absolutely. We've got some new new types coming down the pike as well. We have a dictionary type for more unstructured key values, as well as a set we're looking to introduce very shortly and a mixed type as well, which I believe we have a different name for that. Don't we, Jason?
Jason Flax: Yes, it will follow-
Ian Ward: Any Realm value. There you go.
Jason Flax: ... what that does yeah. Any Realm value -
Ian Ward: Yeah, so we had a lot of feature requests for full tech search. And so if you have, let's say an inventory application that has a name and then the description, two fields on an object and that's a string field. We have just approved our product description for full text search. So, you'll hopefully be able to tokenize or we are working towards tokenizing that string fields. And so then you can search that string field, search the actual words in that string field to get a match at index level speeds. So, hopefully that will help individuals, especially when they're offline to search string fields.
Jason Flax: That's Richard's dictionary would be huge. Yeah. We're excited about that one. We're probably going to call it Map. So, yeah, that's an exciting one.
Shane McAllister: Excellent. Ian's squeezing in a question there, a feature request actually. Leads open multiple sync Realms targeting multiple partition keys. Okay.
Ian Ward: Yeah. So, we are actively working towards that. I don't know how many people are familiar with Legacy Realm. I recognize a couple faces here, but we did have something called query based sync. And we are looking to have a reimagination of that inquiry-based sync 2.0 or we're also calling it flexible sync, which will have a very analogous usage where you'd be able to send queries to the server side, have those queries run and returned the results set down to the client. And this will remove the partition key requirement. And so yes, we are definitely working on that and it's definitely needed for our sync users for sure.
Shane McAllister: Excellent. That got a yay and a what, cool emoji from Ian. Thank you, Ian, appreciate it. Excellent. I think that probably look we're just after the hour or two hours for those of you that joined at the earlier start time that we decided we were going to do this at. For wrap-up for me at, from an advocacy point of View, we love to reach out to the community. So, I'm going to plug again, developer.mongodb.com. Please come on board there to our forums and our developer hub, where we write about round content all the time. We want to grow this community. So, live.mongodb.com will lead you to the Realm global community, where if you sign-up, if you haven't already, and if you sign-up, you'll get instant notification of any of these future meetups that we're doing.
Shane McAllister: So, they're not all Swift. We're covering all of our other SDKs as well too. And then we have general meetups. So, please sign-up there, share the word. And also on Twitter, the app Realm Twitter handle. If you enjoyed this, please share that on Twitter with everybody. We love to see that feedback come through and we want to be part of that community. We want to engage on Twitter as well too. So, our developer hub, our forums and Twitter. And then obviously as Jason mentioned, the round master case or open source, you can contribute on our repos if you like. We love to see the participation of the wider community as well, too. Ian, anything to add?
Ian Ward: No, it's just it's really great to see so many people joining and giving great questions. And so thank you so much for coming and we love to see your feedback. So, please try out our new property wrappers, give us feedback. We want to hear from the community and thank you so much, Jason and team for putting this together. It's been a pleasure
Shane McAllister: Indeed. Excellent. Thank you, everyone. Take care.
Jason Flax: Thank you everyone for joining.
Ian Ward: Thank you. Have a great week. Bye.