In this episode Donn and Kaushik have an honest discussion about Realm (a client side database). Donn has worked pretty extensively with Realm and even consulted for their Android platform previously. But Kaushik has only recently tried it out on a production app.
In this episode they have an honest and frank discussion about using Realm, the advantages, the disadvantages, the gotchas and if it makes sense to use a database like Realm as an Android developer today.
- Fragmented episode with Realm Android developer Christian Melchior
- Sqlbrite by Square (Reactive SQLiteOpenHelper wrapper)
- StorIO: wrapper for SQLite or ContentResolver
- Feature: auto updating objects
- Fragmented episode on GC vs Reference counting
- Realm RxExample
- The problem with Threads – Edward Lee
Github issues tracked on Realm
- Request: Compound primary key support
- Request: Value object support
- Request: Custom column names
- Feature: Copy from Realm
BuddyBuild is a continuous integration and continuous deployment system built specifically for mobile developers. Thousands of development teams love BuddyBuild because it’s the fastest way to build, distribute and gather feedback for their apps. Give it a try for FREE at buddybuild.com
Donn Felker: So, Kaushik, we don’t have anyone on the show this week.
Kaushik Gopal: No, we don’t, so we thought we’d catch up on these chats we have all the time. We keep telling folks, “Donn and I talk about this all the time, and one day, we’ll do an episode about it.” This is one of those days. We’ve decided to capture one of our chats and push it out as an episode.
DF: Yeah, for folks who don’t know, we usually have chats after we hop off the call for our recordings. We’ll intend to chat for about five to six minutes, full of catching up, saying “Hey, I’ll do this and you do this,” and other follow-up stuff—but then it turns into an hour-long chat about technology, and we think, “Well, that would’ve been great to record.”
KG: That has happened way more often than you’d think.
DF: Exactly, so we’re hoping to capture more of that today. One topic that’s come up a few times in those chats is Realm.
Recently, my team at Instacart started to experiment with Realm for one of the apps that we make. I know that you have a lot of knowledge about Realm and have worked very closely with that time, and I had a lot of questions, so I pinged you a bunch of times. I also pinged the Realm team a lot. Several extremely interesting things came up, so I thought, “Hey, why not just hop on a call with Donn and see where it goes from here.”
DF: Definitely. I’ve been using Realm for a while. In fact, we first talked about it back in one of our first few episodes. We mentioned it in the “Awesome Picks” section we used to have. Ever since then, I’ve been highly interested in it, because I come from with Rails (and a few other programming languages that have some kind of active record system built-in), and I’ve always wanted that functionality with Android. I’ve tried other ORMs, but none of them ever felt like they were “it”, even though they did a good job. I never felt like, “This is awesome!”
But right out of the gate, I was attracted to Realm. Like you said, I started working with it for a while, building it into various apps. I recently released another app that’s gaining a lot of traction, and it’s all Realm-based.
For me, it makes sense to use it because I like it and I’m very familiar with it, but what got you thinking, “You know what? It’s time to start evaluating other data persistence mechanisms”?
KG: At least in the app that I experimented with, we started out with a flat JSON structure. Any persistence that we needed was serialized into a JSON file and stored in the external storage area. This was done for many reasons. Historically, when we started out, we decided that we didn’t want to use a full-blown database since only a few models—the user preferences, and maybe one other object—were persisting. In those early days, our app was also heavily driven by a web-based architecture. Essentially, it served as a shell for a glorified web service. It wasn’t a
WebView, but it used a lot of APIs. Pretty much everything came down through APIs. Essentially, every screen was an API hit, because the state was managed on the server side.
For the longest time, that structure worked out pretty well for us, but it got to a point where the app was far more sophisticated than that. Also, there’s been a shift recently towards thinking about mobile devices as mini computers, because they’re pretty powerful. You can do some very cool stuff with them. So, to get the best experience, we wanted to move toward doing everything on-device.
The flat JSON structure we used worked pretty well, if you think about it in terms of performance. Realm has amazing performance compared to SQLite and a bunch of other alternatives. That’s one of their tagline features, in fact. They say, “Hey, we’re way more efficient than any alternative you can use.” For the most part, I think that’s completely true. But in terms of performance, I still think that a flat file structure is comparable to (if not better than) Realm. Again, there are many caveats. It depends on how deep your structure goes and how complicated the serialization is, so take that with a grain of salt. But at least in my experience, I’ve found that they were similar in terms of performance.
So what really won us over was when our objects got complicated enough that there were two relationships between them. There were nested objects. To give a simple example, think of a chat conversation, which has chat messages. Those messages might have small sub-objects—say, different kinds of messages. Anyway, it was getting to the point where relationship queries were a little more difficult. When we used a flat JSON file structure, we essentially had to write the entire ORM layer ourselves. We had to write queries, and really dive into serializing those objects, and filtering them with list queries. It was getting a little crazy, and we were thinking, “At this point, we should be exploring a database for our convenience.”
I know you talk about Realm a lot, so I said, “Hey, this is a great opportunity, because we don’t want to lose the advantage we have in terms of performance.” That’s what got me started.
DF: Yeah, I think it’s important to note that there are ways to both use and misuse various tools. You can use Realm incorrectly and make it slow. You can use SQL incorrectly and make it slow. On the flip side, you can optimize the various SQL ORMs to be very fast. In other words, we’re not trying to say, “You have to use Realm!” Use whatever tool is right for the job. If you’re familiar with SQL, and you want to use a SQL RxJava client, do it. If you want to use an ORM, do it. But if our conversation sparks your interest, then give Realm a try.
KG: This is one of the reasons that SQLBrite was created. The developers got comfortable with writing SQL queries, so they added that layer to make it reactive. That’s also gotten a lot of traction.
So, what was your tagline feature? What made you think, “Okay, this is why Realm is really cool”? Let’s talk about what really won us over, in terms of development.
DF: The first thing that really struck a chord with me came a couple of years ago when I looked at the first example: I would be working with straight Java objects. I didn’t have to write in another language. A lot of times people who are familiar with both SQL and Java will think, “They kind of work together.” Not really. There is an adapter built between the two systems, but SQLite runs on its own engine, and then Java code runs your Android application. You still have to write SQL (Structured Query Language) code to talk to the database.
With Realm, I don’t have to do any of that. I just say, “Hey, here’s an object” and use the
.save() method—and suddenly, everything is in the database. If I want to get it back out, I say, “Hey Realm, go get all objects from the ‘person’ class, and find the guy whose name starts with ‘Donn’.” Then it gives me that object back, and I can work with it. And I thought, “Well, that’s awesome.” That was the initial spark that got me going.
What really solidified it for me was one of those side projects that we all have. I fired it up and said, “You know what? I’m going to try Realm.” It was a simple to-do list app, so I said “Alright, I need a database,” and I created a couple of objects. Then I needed to save them, and later retrieve them. I thought to myself, “I spent just 15 minutes learning this and setting it up, and I’m already saving data. I don’t have to do anything else.” That’s what blew my mind. It was way more productive than building a SQL table, making sure to hydrate, setting up
ContentValues, and deciding whether to use a content provider, a loader, or something else.
So what interested you? I mean, I’d already been in your ear about it before, but other than that, what attracted you?
KG: The simplicity was definitely a big win. Like you mentioned, the fluent interface made it so easy to work with queries.
DF: For folks who’re wondering what he’s talking about, when you query a Realm database, you basically say, “Hey Realm: go look for the people in the person class. I want you to find all the people whose name starts with ‘D’.” You literally just chain these operations together, and you get back a list. And it’s not just a
List<T>. No, it’s a
RealmList, which is a souped up version of
List<T>. It has a bunch of extra features for Realm itself, so you can do updating and so forth. But it just makes it so easy to query.
The other thing that I really loved about Realm when I first started was that everything was done on the main thread. I’m sure villages are burning down right now, as I say that! But it was so fast, and they said, “Look, you can just run this on the main thread, because it’s fast.” We can get into why that is shortly, but I tried it, and it was so fast that I never had any problems. They now have an asynchronous API, which can do things on a different thread and return them to you, and their advice is to use that if you’re concerned with it not being fast enough. But, to make a long story short, I was blown away by how fast it was.
There’s also the Realm Browser. Realm is pretty popular in iOS, so let’s say that you go to a company, and they say, “We have this iOS app, and we need you to create an Android version.” That happens all the time. Well, if they’re using Realm, they can actually open the Realm file inside of the Realm Browser (which only exists on macOS right now), and use the file to create the basic Java classes for the app. The browser will generate all of the classes for you, so that can easily share modeling between the two different apps.
KG: I read about that, but I didn’t realize that that was the benefit. That’s pretty slick.
DF: You’re not really sharing between two different platforms (you’re just sharing the model files), but it helps you get jump-started. Android and iOS developers are probably going to model things a bit differently, but if you’re building an app with a lot of models, that’s still going to help you jump-start your application.
So I saw all of these things, and I thought, “Wow, this is pretty freakin’ cool.” I didn’t even learn about the reactive features that really make Realm shine until I actually started working with their team. We’ll get into those shortly.
KG: That’s definitely something I want to talk about, because it’s essentially what won me over. I basically said, “We’re definitely going to Realm because of the reactive architecture.”
But to step back, you mentioned the fluent interface and queries. Now, some folks might think, “Is it really that tricky in SQL? Is there really any benefit to that?” There was one query that I wrote that made me think, “Wow, this is so much easier.” When you have a list and you want to negate queries, you use not equal to. It’s a very common condition. For instance, “Pick all the messages that aren’t read,” or something along those lines. When you want to run the not query in Realm, it’s done beautifully. I was really surprised, because all you have to do is throw a
.not() on any condition inside a
If I had to implement that back in my file structure days, I’d have take that list and run a filter on it. I’m not saying it’s difficult to do, but Realm makes it ridiculously simple. In the end, that’s what we want. We don’t want to spend our time filtering lists, taking lists, converting between arrays and lists, and making sure something is an array list. We shouldn’t have to deal with such simple things.
DF: Like you said, you can filter your code, but you’d end up checking nulls and doing sorts of other stuff. You actually end up with quite a bit of (very simple) plumbing code to get the data that you need. Like you said, if you’ve set your column width to be wide enough in Lint, you could (quite literally) fit the whole query for Realm into one line of code. You just say, “Realm, get me all of the people whose age is not less than 5.”
KG: Their fluent API works really well.
When you brought up the reactive architecture, you said that
RealmLists are just a really souped-up version of
list<t>s—as in, they have some crazy features. This is another thing that won me over, so let’s spend some time on it. It’s really cool, though I don’t think it gets much credit in the documentation. When I actually saw it working, I thought, “Whoa, this is mind-blowing.”
DF: I’m not consulting with Realm anymore, but I was at this time last year. And last November or December, I read through the documentation, talked to a few guys on the team, and learned about
RealmLists. And I thought, “Whoa! This is actually the really sweet stuff in Realm, and nobody really knows about it.” They call it the “reactive architecture” or the “auto-updating objects”.
I’m going to give an example here. Let’s say that I have a list of ten person objects in a database. Then I make a query that says, “Hey Realm: go get me all the person objects whose age is greater than 18,” and I get back a
RealmResult with 5 people in it.
Now, for some reason, let’s say I have an Android
IntentService doing something else in a different thread in the background. Maybe it’s doing a pull from my server to check for new items or respond to a push notification. All of a sudden, it says, “I need to update some data in the database,” so it pulls down some stuff from the API and starts updating the Realm database. Suppose it adds another person to the person table (if you want to call it that). That person is age 30, so he or she should be included in that query now.
Behind the scenes, Realm updates the list for you. It’s actually updated on every iteration of the Android run loop, so you can attach a change listener to an object, a
RealmResult, or even the whole Realm database itself, and say, “Hey, any time this data is updated, call me back.”
KG: I didn’t really understand this until I saw it happen. Folks, you can attach listeners to your whole database. You can say, “Any time anything in my database is updated, notify me.” That’s crazy! Whether you should really be doing that is another issue, but the fact that it allows that is pretty cool. You can also attach a listener to this object—say, a “persons” table: “Anytime anything changes in the persons table, send me a change notification.” You can even attach one to a query. You can say, “Hey, I’ve executed this really complicated query where I’ve negated all of these conditions and get a list back. If something in that set of elements changes, send it back to me.”
DF: Yeah, it could be an object getting updated, a new object being inserted, or another being deleted. It’ll say, “Hey, something has changed in this query result, so we’re going to call you back, and you can do something with it.” It’s completely reactive. You aren’t pulling the database. Instead, the database itself is actually telling you, “Hey, this changed. Go do something.”
KG: When I heard about this in a casual conversation, it didn’t really hit home. I thought, “Oh well, I get that with RxJava anyway. I mean, I can build that architecture myself, so why is it really that amazing?” It took me some time to understand why it was so cool.
DF: One of the reasons why it was so cool to me was that I could actually create essentially a unidirectional data flow application. For example, let’s say that I’ve made a kiosk application that’s running in the mall, showing data about how many people are currently in the building. As they walk by a sensor, it updates the data in the database, and everything happens in the background. Your application doesn’t need to do anything. It just respond to these notifications. All of a sudden, everything is super reactive.
On top of that, if you’re just using the core Realm concepts and you want to use something like Rx, you can. They actually have Rx bindings. Have you used them at all yet?
KG: Yes, those are basically what I use, because I’m very comfortable with Rx. I try to switch an element morphing to Rx, as a personal preference, because I understand it decently well. I feel very comfortable with it because I understand the things that happen in it.
The interesting thing about this is that you can attach to it as a change listener or to the first-party bindings (as you said), so you can switch directly to Rx and still get all of these benefits. The reason I think that’s interesting is because if I did have to implement this in Rx—which I could totally do—the complexity arrives in two levels.
- I’d have to keep executing the query. Otherwise, how else would I know that the database had changed? Alternatively, I could use another option, like SQLBrite or StorIO.
- Secondly, I might have to use something like a Subject, which (again) you can do if you understand Rx well enough, but it’s still boilerplate that you have to write manually.
These are both things that you get automatically, for free, by using Realm. The first-party bindings are as simple as adding
.asObservable() Boom! It’s converted to Rx. It’s funny that the example you gave was so similar to what I wanted to achieve, which pushed me toward using Realm in the end.
At Instacart, we had this chat conversation model. As our customers are navigating and doing things on their screens, if they let us know about a change that they want made to their order, it’s extremely important for us to get this to the shoppers. So we basically had a shopper/customer model: we want the shoppers to immediately see any customer notifications.
I’m not going to go into the details of the business logic, but it was extremely important that, no matter what activity or screen shoppers were on, that message needed to surface immediately. That was ridiculously simple to implement. All I did was create a
RealmObject for the chat conversations, and any time it updated, it would know immediately and pop up the unread indicator. All I did was write one-time logic to query the model, and then I said, “Hey, listen to this model on the screen.” Boom! I got that for free on every screen.
DF: I think you mentioned this already, but I want to make it very apparent: if you’re querying Realm and you want to get an Observable back, you literally just add a method on the result that says
.asObservable(). It gives you an Rx observable, and from then on, you’re off to the races with Rx.
KG: That was really compelling to me, because having to implement this by hand is pretty tricky. But there were some other things that I found interesting. This is a little more meta, but the way they do their development really impressed me. Realm is entirely open-source, and they don’t just do that as a way to get brownie points from developers. As I got interested in Realm, I started to poke around in the code and the GitHub issues to see how things were done. There are some things in those issues which are still lacking in Realm, and we’ll get to those shortly. But any time I had a doubt, I would do is hop onto the GitHub database and look around. It’s impressive how responsive the core team is.
In fact, that’s another thing I wanted to talk to you about. What are your thoughts about the core team and how they do their open development? You’ve worked firsthand with them, and I’ve seen a couple of your comments in the database. So, how has that been?
To me, it’s really impressive. They’re very structured, and it seems like a no BS situation. They’re not trying to impress you. They’ll say, “Hey, this is in our road map, so we’re going to do this.” Or, “Okay, these are the different points which we have to think about. We have these other considerations, so how can we attack this?” They’re very, very clear, in terms of their process.
DF: Interestingly, they’re actually quite distributed, geographically. They have offices in San Francisco and Copenhagen, but (as he said in our previous episode) Christian actually lives in a totally different part of Denmark. Long story short, developers all over the world work for Realm, and they do it very effectively. They do all their development right out in public, so if there’s something that needs to be done, like a new feature idea, they’ll throw it right there in the GitHub issues.
In fact, if you ever have a problem, the best place to go is the GitHub issues. Search through those to see if it’s been solved, because someone has mostly likely already asked the question. They have an internal Slack group where they discuss new tickets as they come up—”Alright, how should we handle this one? Okay”—and then boom! one of the team members replies, if someone hasn’t already. It’s very, very interactive, and they take every single thing from the community very seriously.
You’ll see lots of issues linked to other issues: “Hey, we’re covering this over here.” In fact, a lot of times when I have an issue that I can’t figure out, I’ll ping one of the guys on the team and say, “Hey, I’m trying to do this,” and the first thing they’ll do is link me to a GitHub issue which I didn’t find and say, “We’re already handling it right here,” “It’s coming,” or “Yep, we fixed that two weeks ago. It’s in this release.”
They do this with all of the bindings: with Realm Java, Realm Cocoa, Realm Core, etc. They have discussions right there. Sometimes, they’ll do planning right there in the GitHub issues. They’ll say, “Okay, here’s what we’re thinking. This is the current plan.” They’ll even create little example test branches and push them, saying, “Here’s what we’re thinking. Please provide feedback.” Then the community hops in and interacts from that point onward.
Furthermore, they’re also very active in different communities. If you’re in the various Android development Slacks, their folks will sometimes be in there. They’re also very active on Stack Overflow. If you happen to post a question there, some of the team, like Christian and Emanuele Zattin, are answering those questions. So they take everything seriously, and it’s fantastic to be a part of it. If you’re ever interested to see how things are done there, just hop into the Realm issues, and you’ll see a whole bunch of stuff going on.
KG: I think they do a phenomenal job. It must be said that these folks who work at Realm are some extremely smart people. People sometimes ask me, “Hey, why do you always recommend Square libraries? There are other libraries.” I always answer, “There are, and if they’re good, I’ll start recommending them. But I’ve noticed this bizarre correlation that when you have super smart people working on a library, it tends to be pretty good.” I think that holds true here.
Some of my friends have mentioned that Realm also has some open-source libraries for the iOS platform—for example, a Swift linting library that’s apparently pretty well known. They know what they’re doing.
Also, a lot of people who are reading this have probably videos on the Realm website. They’re very active in recording lessons and giving content back to the community. That’s been a big help.
But to rewind for a second to your point about intelligent folks, I can’t agree more. I was talking to some of the core guys, learning about some of the internals while I was helping to write the documentation, and some of the things that they explained to me melted my mind: “What did you just say? Whoa, that’s crazy!” They’re very intelligent.
Now, you actually learned a lot by just browsing the repositories, right?
KG: Absolutely. If I wanted to understand how something worked, I would just hop onto certain issues. The other funny thing is how they would cross-link between repositories. Sometimes I would see an issue that said, “Hey, this happens because of x in Realm Core, so we’re waiting on that app.” Then they would link to the other issue, and I would follow the thread.
Just going through the repository is immensely helpful. That’s the advantage of open-source. I mean, many folks in our community can see the open-source code (even if it’s in the AOSP) and understand why a certain thing happens. It works really well: you have the code, and you can just dive in and see what’s happening. That’s the truest form of documentation.
DF: But by the same token, it’s also a huge problem: some things are not as exposed as well as they could be. You can hop into the code and say, “Oh, that’s how it works,” but it could have something that could have easily been exposed through the documentation. That’s one of the downsides of Realm: sometimes, there’s so much going on that you don’t even know something is there. Again, I know from speaking with the team that that’s something they’re working on. But if you do want to understand it, as I said in a recent tweet, go look at the source code.
If you have a problem, they’re also very open to bug reports. Look at the contributor list—it’s massive.
KG: At this point, people might be reading this and thinking, “Hey, these guys are really pushing hard for Realm. What’s the deal with that?” It isn’t all sunshine and roses. Even to this day, there are some problematic areas, and I think we should spend some time on that.
DF: Definitely. What are some of the things that you ran into, and felt like, “Okay, this is still a little painful”?
KG: The first thing I wanted to bring up is actually the primary key situation. Like an database, Realm uses primary keys. The strange thing is that they don’t have compound primary keys. What are compound primary keys? Let’s say you have an object and you select “ID” as your primary key. All is good there. You just slap on the primary key annotation, and you’re ready to go. But if I wanted a combination of two keys—for example, a person’s name and email address—to be primary keys (which is very common), that’s not possible. There is a workaround, but you can’t just apply the primary key on more than one attribute. Realm is not happy with that.
I found that very perplexing. I thought, “I don’t understand the internals, but that isn’t too big of a thing to ask. I want a combination of two fields to be the primary key.” I found that interesting, because my use case involved a model that comes down from a server, so I don’t necessarily dictate what the ID is. The server sends back a JSON response which says, “Hey, the combination of this type and this ID is unique. The ID alone is not unique, so you have to take into consideration the object’s type.” That basically means that I need a compound primary key, but I couldn’t directly achieve that with Realm. What are your thoughts on that?
DF: That’s something I’ve seen a few people complain about as well.
The other thing that really bothered folks for a long time is that you have to extend
RealmObjects. A lot of people don’t like being forced to extend anything that they don’t create themselves, but that’s how a lot of the bindings were created. A lot of the stuff was done in an annotation processor, so the code was written for you. That’s where you get all of your helper methods from: “Hey, is this object valid? Can I find this query? Can I delete the object?”
They’ve recently implemented the
@RealmClass annotation, so you don’t have to extend
RealmObjects. The only difference there is that you have to use other helper methods (the Realm static methods) to actually get things working.
Then there’s the thing that a lot of folks are very heavy on using: value objects, which we recently talked to Ryan Harter about. That’s not implemented yet. There are a bunch of tricky things going on if you want to use immutable value types in Realm. It’s very interesting.
Have you noticed anything else? I remember you talking to me about getters and setters at some point. Did you ever work that out?
KG: It’s kind of funny, actually. This is very, very perplexing when you start up, but they have it in their FAQ, so if you look through their documentation, you’ll reach this at some point. Suppose I’m debugging an application. If I’m in a class, and I want to check what the value of an object is, I’d normally pull the object and add
.name(). For instance, if I have a variable called
m_person (I’m using Hungarian notation here, just because it makes it easy to explain), I’d say
But in Realm, it will return null. In fact, all of the values will be null. And you’ll wonder, “What am I doing wrong? Did I understand Realm incorrectly? What’s happening?” It took me some time, but I eventually realized that it’s a very different model (this is something you should note) where getters and setters are actually pretty important. They’re not just regular, POJO getters and setters. Realm has this thing called a proxy class that works a little differently. Instead of saying
m_person.m_name() and accessing the variable directly, if you say
m_person.getName() and use the actual accessor method, you’ll find the value.
DF: This actually segues back to the previous topic about how Realm is so fast. I’m gonna give you the 30,000 foot view here. When you’re working with one of those objects and you say, “Hey, get me the name of the object,” it actually reaches directly into the Realm database to give you that value. It’s not querying the database, loading it into memory, putting it into some buffers, copying it over, and finally bringing it to you (and by then the data is out of date). It’s actually reaching directly into the database and saying, “Give me that name.”
If something was to happen in the background, and the name changed from “Donn” to “Kaushik”, and you pulled it both now and two seconds later, when you pulled it again it would say “Kaushik”. You’re not working with a copy, but with the real database. Besides, all of that code is inside the core, which is C++ from the ground up. That’s another why it’s so fast. So it’s literally reaching directly into the database, and the proxy helps get that done. That’s probably one of the reasons you saw that.
KG: That’s so interesting. I didn’t realize that. It’s almost like you get the best of both worlds: it’s lazy loading (in some ways), but it’s actually direct loading. Now that I realize what that is, that’s actually pretty cool. But still, watch out for that.
One quick follow up question: since you typically use Realm, do you use the composition style or the inheritance style? Do you extend
RealmObjects, or do you use
DF: I’ve been using Realm since before the
@RealmClass annotation was around, so I just use inheritance.
KG: That’s another thing. I’ve noticed that some things just inherently work better if you extend
RealmObjects. For example, if you don’t want to have getters and setters, and you just want to have basic public fields, I’ve found that it tends to work better with a
RealmObject. That might just be a bug or something, and I apologize because I don’t remember the specifics. Also, if you just stick to using getters and setters and use it as advertised, it works without too many issues, but the minute you start to customize the objects, you run into issues.
DF: I stick with inheritance. It works for me. But there are some folks who do prefer the
KG: I use
@RealmClass myself. One of my reasons for that is that I already had a model defined in POJOs in my application which already implemented certain things and extended certain classes. I didn’t want to complicate those things, so I just slapped on the annotation that said “Hey, this is a
@RealmClass. Implement the
RealmObject.” Basically, that was an easy way for me to not disrupt the existing state of affairs in my application and still get going with Realm, so I stuck to just using that.
DF: That’s a very good point. You don’t have to go all in when you want to try out Realm. One of the best use cases I’ve seen is exactly what you’re talking about. Usually, you have a couple of objects and some type of state persistence—say, a local cache. That’s a perfect time to try out Realm. Just throw it in there and use it for the cache. A cache is also a perfect example, because you need to know when it’s invalidated or updated. You can attach those change listeners, and get that reactive nature from it. If you want to, you can also take your existing objects, throw the
@RealmClass annotation on them, and use them to test it out. That’s a great introduction.
KG: Speaking about gotchas, the other thing that got me multiple times was the whole concept of the Realm instance closing. Correct me if I’m wrong, but this is what I’ve noticed in practice: essentially, any time you open up a
RealmObject, you also have to close it. That’s a very traditional, database transaction level thing.
Initially, when I started with Realm, I thought there was some magic happening, but it isn’t magic. It’s just basic reference counting, which is actually pretty cool. They don’t have a garbage collector. We actually did an episode on the difference between garbage collection and reference counting a while back, so you can alway give a listen to that. Since Realm is reference counted, though, if you open up an instance, you need to make sure that you close it. If you don’t, you’ll start leaking memory.
DF: Usually, I’ll just wrap it in a try/catch. At a higher level, I’m also using Dagger. Some people may agree or disagree with this, but I actually have one instance just for the main thread. I create that, and then I can inject it anywhere I want as long as I’m on the main thread. I’m careful about staying there, which we’ll talk about in a second. When I’m using it, I make sure I’m on the main thread, and I don’t use it anywhere else. Then, if I need to use anything in a background thread, I’ll make sure to open, close, and so forth from that point.
KG: I really want to ask you some follow-up questions on threading, because I think that’s super interesting. Like I mentioned before, I’m using Realm in conjunction with Rx. It gets really tricky, because when it returns the observable of elements, it’s returning a
RealmList or an observable of
RealmObjects. That really got me, because I didn’t understand how to close it. The problem is that when you expose an observable, you’re going to use those elements. But if you closed the Realm instance inside the observable, like you’re supposed to, any time you access an element, it throws an exception, saying, “Hey, this Realm instance has already been closed. You can’t take any more actions on top of this.”
So it took me some time. Again, because I’m comfortable with Rx, I know the workaround here. For folks who are curious, you just add an
.onTerminate() call. The advantage of observables is that you can say, “Hey, execute this when you are done with the observable,” as long as you make sure that you’re terminating the whole composite subscription correctly. If you do that, you can say “do
.onTerminate(),” and close the Realm instance inside that.
DF: We’re really getting into the weeds of RxJava implementation here. If you’re interested in how this works, Christian actually created Realm’s RxJava example. He does a bunch of stuff in that where he’s in the observables, and so forth. You should definitely check that out.
KG: Let’s go ahead and talk about this threading thing. This is another very interesting aspect of Realm. Whenever you create a Realm instance, it’s tightly bound to the thread that it’s created in. You mentioned that you can inject a Realm instance that’s linked to the main thread through Dagger. Can you tell us a little more about this?
The reason for this binding, I believe, is because if you try to access the same
RealmObject on a different thread—say you’re between a Rx call, and you hop onto your I/O thread and access that Realm instance—it will blow up on you.
DF: Exactly. Multi-threading has been the elephant in the Realm room for the longest time. As I said before, you can run everything on the main thread, and it’s still fast. But the reason why Realm is basically thread-bound is because multi-threading is hard. There’s a white paper written by Edward Lee (a professor at the University of California at Berkeley) called “The Problem with Threads“. This is a little snippet from it:
Concurrency in software is difficult. […] non-trivial, multi-threaded programs are incomprehensible to humans.
Threads run over a period of time. If you put them on an x/y axis, left to right being time-bound, and if you have multiple different operations happening at different times—well, just try to get three in your head. You can probably manage that okay, but you’ll still get some bugs. When you get over three, the Cartesian product suddenly becomes insane. You’ve got 16 different possible combinations of things that could happen at any given time. Add another one, and it’s even harder. It just gets crazy.
So, how have previous databases solved these problems? Well, they’ve normally just applied locks. Let’s take a typical example: I want to read from a database, but I’m going to make sure that the data is consistent. If someone is writing to the database at the same time, I may get inconsistent data. I might ask for one thing, and something else may come back. How do we solve that? Typically, we throw a lock on it: I can’t write to it until someone else is done reading from it, and vice-versa. So you end up placing locks all over the place.
And so the big sell for Realm with this threading lock comes from the documentation, actually. This is a quote that I got from Alexander Stigson, one of the co-founders of Realm:
The only limitation [of Realm] is that you cannot randomly pass Realm objects between threads. If you need the same data on another thread you just need to query for that data on the that other thread. Furthermore, you can observe the changes using Realms reactive architecture. Remember – all objects are kept up-to-date between threads – Realm will notify you when the data changes.
It’s basically saying, “Look, we can get rid of all the threading problems that you’ve had. The only limitation is that you need to make sure that you’re using the same Realm instance on every thread.” Each Realm instance is thread-bound, meaning that you can have a Realm instance open on Android’s main thread. But if you’re watching some data to update my activity, or if you have an
IntentService updating data, you’re going to have to open a different Realm instance in that thread. You’ll have to call
Realm.init(), which will give you a Realm instance. You can work with it, and when it’s closed, Realm will sync up in the background, because it’s a multi-version concurrency control database. In other words, at any given time multiple versions of the database are running, and everything gets squished together and brought up to date on the next run loop.
KG: That’s pretty complex. I remember reading an article or seeing a talk about this, but I just want to highlight a point that you mentioned. You put it beautifully: the locks are a problem, but you don’t want a situation where the data you’re accessing changes underneath you. Realm solves this problem. It’s a trade-off, basically. All you have to do is make sure that the Realm instance you’re accessing belongs to the same thread, and it will handle all the other problems for you, even on other threads. It’s the same concurrency problem that you mentioned. Did you update data on a background thread? Your current thread handles that. You aren’t going to get stale data. You’ll get the most up-to-date data when you access that object again. The only limitation is that you have to stick to the same thread or Realm instance. I just want to call that out, because it’s a really good thing.
DF: One of the drawbacks of Realm is that it takes a complete paradigm shift to work with it, especially if you’re used to working with Rx, where you’re just tossing around threads left and right, and changing them around. You can’t really do that in Realm. You’re have to think, “Okay, I need to stay on this thread if I’m going to work with this data. Then, when I’m done, I can do something else.” If you don’t remember that, you’ll get into those weird instances where you get the error you talked about: “Hey, the Realm instance has already been closed. You can’t do this operation on this object.” But Realm solves all of those huge problems for you: “Hey, don’t worry about the threads. Do it on the same thread, and we’ll take care of everything else for you.”
As developers, it’s hard to let go sometimes. We’re control freaks.
KG: I had to wrestle with that too, but the explanation that you just gave me makes so much more sense. I understand that it’s a trade-off, and I’m willing to make that trade-off.
DF: Yeah, because you otherwise just get locks, and that’s slow.
KG: Let me give you another example where I found this to be tricky. I usually like to model my architecture like the repository pattern that some people follow. Essentially, I have a POJO—a DB service—and all my database operations are relegated towards this service. The reason I do this is to make unit testing easy. In my unit tests, I can mock out this DB service object and swap it out with elements, saying, “Whenever anyone calls you, return this set of elements.” That very typical setup allows me to do proper unit testing.
The thing people have to realize is that Realm works very closely with Android. In fact, you need an Android framework to work with Realm. You can’t spin Realm on a pure Java environment. That can get a little tricky, because what am I going to do in unit tests? I don’t have an Android instance. I’m running this on the JVM.
To give you an example, a typical query that I might like to execute is
dbService.getPeople(). The trick here is that it returns a list of
RealmObjects. Those are (again) bound to the Realm instance, so even my unit test isn’t going to work. There are multiple problems, and it also points to the closing of the instance: “At what time do you close the instance? If I have the dbService object return the results, and I’m accessing it at multiple points, then how do I know when to close the Realm instance?”
One way I could avoid that is by passing in a Realm instance and saying, “Whenever you query, use this Realm instance.” Then, at the activity layer, I could call
onResume(). Many of the examples that I see follow this pattern. Then I’d create the Realm instance in my activity, and pass it down the chain all the way to the DB service object. I’d use that, and then make sure that the object is closed in the end. Personally, I feel like that’s a little clumsy. It feels like I’m adding way more boilerplate than is necessary.
But I did find a workaround eventually. This (again) points back to the documentation not necessarily highlighting things in a way that makes sense. Inside this DB service object, I would open the instance, execute the query, and then close the instance. I would try to contain it entirely within the DB service object, and I would instead return a copy of the object. I wouldn’t return the exact
RealmObject, which is being pointed to at the source of the DB, but rather a deep copy.
Let’s say I have this “person”
RealmObject. All I want is a similar Java object that’s one-to-one, but not linked to Realm. If I could make a copy of this, that would work, because the responsibility for what goes out is mine. Whatever comes out of the DB service object is just like any random Java object—I can do whatever I please with it.
It took me some time, but I eventually realized (from a GitHub issue where Christian pointed this out) that Realm has a really cool operation called
.copyFromRealm(). I believe this feature was requested and added later, so I’ll make sure to add a link to that GitHub issue. When people asked for this, they said, “Hey, all I want is just a Java copy. Sometimes I don’t have access to the
RealmObject in a typical use case for unit tests. All I want is a Realm copy, because I’m not necessarily testing my DB service. I’m testing something else that requires the DB service object.” This copy from Realm does just that: it creates a deep copy. It says, “Okay, I’m going to create an exact copy of this object, and then I’ll give it to you and you can do what you please.”
Essentially, that’s what I did. It was really easy, because I’m using an Rx architecture. I just slapped on an operation saying, “map, and
.copyFromRealm(),” and boom! I got a copy, and everything is still contained in my DB service object. It took me some time to understand that, but it now works perfectly well. I don’t even have to worry about creating and closing that activity. I just create the Realm as and when I need it.
DF: The only issue that you’re going to have with that is that those are unmanaged deep copies of those objects, meaning that Realm is not watching after them anymore, further meaning that you’re not going to be notified of any updates to those objects, and they’re going to be invalid at that point. It’s just an in-memory copy of those objects. That’s something to be aware of, since you can’t attach listeners or anything like that to those objects.
KG: What I do, essentially, is expose an observable from the DB service object itself. That keeps returning a new copy every time, so I get the advantages of both in some ways. Internally, I’m using a listener and exposing an observable, so that internal DB service object is getting those changes. There’s a very Realm-like behavior to that.
DF: How many objects are you returning on average from that? Do you know? Is it just a couple? A hundred? Five thousand?
KG: It’s just a couple of objects. If I’m was on a Twitter feed stream, returning a billion objects, I would run into memory issues. So far, though, I haven’t run into a need to use that many objects. But yeah, these deep copies are still a copy, which means more memory.
There’s also a helper, which gets really tricky. If you read the GitHub issue, Christian actually talks about that. You can pass in how many levels of nesting you want, because you can have child
RealmObjects of the copy from Realm. For example, if I had a chat composition
RealmObject which internally had chat messages, those messages might have an image object and a content object, and you’re storing all of these as independent Realm database tables—a typical one-to-many relationship. I could say, “Hey, just get me to one layer. I don’t care about the nested objects.” I think that’s done for performance reasons. I could also say, “Hey, give me everything. I want the truest representation of the object.” Again, this is something that you have to understand is happening.
DF: People are probably wondering, “What if I have a ton of data in my database?” Let’s say I’m using connected, regular
RealmObjects. I’m not using
.copyFromRealm; I’m just doing regular queries. If I create a list and say, “Find all of the persons in the database,” and that’s 200,000 records, please note that you’re not loading all 200,000 records into the memory. Think of it as a pointer down into the databases. I’m currently on this record, and it’s going to pull more objects out as I iterate over them. It’s not loading everything into memory all at once, so it’s very memory efficient as well.
KG: For sure. If you go the traditional route, this thing is crazy optimized, so you won’t have any issues. It’s good that you brought that up.
DF: You also ran into an issue doing a very similar thing where you were trying to read stuff in from your API. You had some situation with weird API naming conventions, and you wanted to change them over. Do you remember what that situation was?
KG: This is one where I couldn’t find a workaround, but I think there’s an issue tracking it. For example, say my API sends down an object. Since this is a typical back end, it’s a JSON. I’ll use the same person example. Maybe one variable is “first name”. The column name would be
first_name—typical snake casing. But that’s very un-Java-like. In my actual
RealmObject, I don’t want to change the name of the variable to
firstName (in camelCase).
The problem with Realm—and this seems so basic that it’s surprising they don’t have it already—is that you can’t have custom column names in your database. You cannot say, “Hey, in my Java
RealmObject, keep it in camelCase, but when you save it in the database, I want it to be named
first_name.” I couldn’t find a workaround for this. It’s kind of funny, because we added it to both the iOS and Android applications, and we were thinking, “Hey, we need to keep this consistent”. This is one point we remembered to touch on, because they have their own mobile platforms. One day, everything can just synchronize across platforms, but I didn’t want to change my variable names.
I couldn’t find a workaround for this, so if you open up the Realm database for my Android application, the column name would be
firstName, which is kind of iffy. But I think they’re thinking about solving this.
DF: What was the value coming down from the API? Would it be
KG: If you had a JSON object of “person”,
first_name would be the key, and the value would be the string “Donn”. Then
last_name would be “Felker”.
DF: How are you storing that in the database now?
KG: I parse the JSON object (and you can use a very typical JSON deserialization library—GSON, Jackson, Mushy, or whatever works for you), and I have to convert that API object to (in this case) a Java
RealmObject, which I can then save into my database. The problem is that when I do the deserialization, converting it from the JSON object
first_name to a Java object, it goes into camelCase, because it’s a Java object at this point. If I provide a Java object and then say
.saveToRealm(), it essentially maps it to the variable name in your Java objects. Essentially, I’ve lost
first_name. My Realm database is in camelCase.
DF: I guess the other issue that I’m having is whether you want it to be
first_name in your database?
KG: I do, potentially, because that would mean that the iOS folks don’t have to have camelCasing. camelCasing is not common in iOS land, I hear. The other tricky thing is that we have some objects which use Hungarian notation, so it’s like
DF: OK, I finally see what you’re saying. You want to keep the naming convention from the API, so that it says
first_name when it gets saved to the database, but in Java, you’d like it to say
firstName. I see what you’re saying. I haven’t had to deal with that, because what I’ll typically do when I get some crazy API is to just save everything in the Java format. If I come down, and it’s
_ID or something, I’m not saving that. I’ll just use the serialized name property of GSON or whatever library I’m using, and it will just change it so that the field is actually called
ID. Then it gets saved in the proper format, and I don’t worry about it from that point forward. But I guess if you’re trying to share these objects across different platforms, that could be an issue.
KG: In all fairness, you’re right. This is a problem that you would see later on, but it’s not something I immediately see. At some point, though, it might get a little tricky.
DF: They are tracking this, again, on the issue tracker, but it’s currently in their backlog.
KG: There was another tricky situation. What was it?
DF: Was it partial updates?
KG: Yes. This is not specific to Realm. In fact, it’s a very common problem. Essentially, I get a new Java object from my API, and I say, “Hey, update the copy of Realm with this.” The tricky thing is that what you’d usually like to happen is for it to only update the non-null values. Usually, your API will only send certain attributes (say, three or four) in order to make sure you don’t have a very big payload. So I’ll use GSON or something to serialize this into a Java object, which works perfectly. Those three fields that I need will get updated as required, so no issues there.
The problem I would run into is that, other than those three values that came down from the API, everything else is null (as it should be in a serialization). In Java, you cannot say, “Don’t have a value”—it’s either null or not null. So, when I say
.copyToRealm(), I’m going to run into an issue where it updates all the other values to null. So I’m getting an API that says “Only update these three values,” but when I actually update my
RealmObject after having serialized that, it wipes out all the other values and make them null.
DF: That can be tricky at first. You’re like, “What? This is not the expected behavior.”
KG: Right. But this is very common too. Initially, when I did a little research, I thought, “Maybe this is something that’s only typical to Realm,” but that’s actually not the case. Many databases have this problem.
It should be pointed out that there is a solution for this, because Christian actually pointed me to one. Realm also has a feature where you can copy a JSON directly into the database. Instead of going through the serialization, you can say, “Hey, take this JSON and straight up map it into my database.” If you provide a JSON object, Realm understands that the values which have not been provided shouldn’t be updated, so that’s perfect for our use cases.
But again, this is the problem that I was telling you I ran into. If you save from the API and copy to Realm directly, the keys from my API object are going to be in
snake_case, which means it won’t find that value when it’s copying into Realm. That’s a problem. So I had to write a minor mapping. I had to construct another JSON object which would map the fields from my
RealmObject, and then use that to copy to Realm directly.
DF: Wow. That’s kind of a pain. You should fix your API (just kidding).
KG: Problem solved, right? But like you rightly said, if you don’t care about these things (like in a typical use case), you won’t run into this problem. But if you want to use a combination of two features, where you want to copy only certain attributes to realm, and you don’t want to change your Java class to have the same variable names as my API, then you run into this problem. I had to work around that by writing a custom serialization mechanism, which serializes it into a JSON that maps one-to-one with my Java object, and then save that into Realm.
DF: So do you guys actually share your Realm databases between iOS and Android, or are you just preparing for that?
KG: That is a good question. We’re just preparing for it. You’re right in calling me out.
DF: It’s just preparing for what you don’t know. Then again, you brought up the point of things we’re used to having in other languages, like an active record. If you wanted to change a database, how would you do that? In an active record, you’d use migrations. Well, Realm actually has the same thing. If you needed to change that column name, you could write that migration in. One of my clients uses fifteen or sixteen migrations, where we add a class, add a field to a class, add a couple other classes, etc. That’s another nice thing about Realm: it’s very easy to update the database, so you aren’t stuck with that same scheme that you started with. You can actually add to and modify it as time goes on.
KG: Speaking about migrations, there is one cool thing that I should point out. Realm has a really cool helper which can delete the entire database if you think a migration is needed. So if you don’t want to maintain the local data, and you also don’t want to deal with migrations, you can just slap that on. Any time Realm thinks that your schema has changed, instead of writing custom migrations (which can get very tricky to understand sometimes), you can just say, “Actually, I want to just delete the database, because my API will pull the objects down and I’ll recreate it.”
DF: Yeah, if you have that ability, you can just rehydrate on the next screen load. That’s such a huge time saver, because if you have to manage that data migration process, it’s not perfect. Data migration is always going to be a manual task, where you’re extracting, transforming, and loading into different areas. But if you can, use those little helpers where you can just delete it and restart. It makes it so much easier.
KG: To your point, if it’s a pure offline app, sometimes you don’t get the luxury of just wiping your local data, because you’ll lose everything. In those cases, you have to go through the migration route.
DF: You bring up a good point. There are a lot of situations involving offline apps. You did this at Wedding Party, if I remember correctly. You guys had an offline app, and you needed to write custom server sync logic, correct?
KG: At Wedding Party, we were very proud of the sync mechanism we wrote, because we wrote it all by hand. It seems like a very simple thing, but it’s not simple, folks. Writing a good sync engine can be ridiculously hard. There are so many considerations to make. You’ve got to make sure that you’re not sending the complete data—just incremental data—but you’ve also got to know which data to pull and which not to pull. Wedding Party had a pure offline mode app where each thing would function independently, but we also had a web application that was the master that all the data would be synced up against.
We can point to some links in the show notes where folks have talked about sync engines. They’re very complicated, trust me. I have written one. It worked really well, but if people came and asked me to write one, I’d say, “No, let’s just pick a solution.” The amount of work that goes into writing a sync engine is almost always not worth it, unless you have a team that’s just willing to work ridiculously hard on that.
DF: That segues perfectly into something that Realm recently released, which I unfortunately haven’t had any time to play with yet. It’s known as the Realm Mobile platform. It’s basically a Realm sync engine with an object store that sits in the cloud. It has two-way data sync, with synchronization and conflict resolution built in for you. You don’t have to build any of that stuff that you fought through before.
They also do a bunch of other stuff, like events that trigger server-side notifications when they need data changes on the server. They have their own custom authentication access control, and (of course) it’s encrypted. I believe it uses AES-256. I haven’t had a chance to play with it, but I really want to. I might do it next week on vacation—just poking around to see what it’s like. If this solves what they say it’s going to solve, I can’t imagine how many man-hours are going be saved by doing this.
KG: That’s another thing. It wasn’t just about convincing myself. I had to convince my team too, because adding a database is not something you simply do on a whim. But this was something that we felt was very convincing. Again, we’re starting to move offline. That’s a big project that we’re doing for our shopper app at Instacart. If we get to a point where we need to sync data between multiple platforms, this makes it ridiculously easy. Again, like you rightly said, I haven’t used it either, but the way it’s being pitched is: “Hey, you have this iOS Realm database and this Android database. You don’t have to write the sync logic. All you have to do is use the Realm mobile platform.” If I issue an update—say I correct the name variable in my Android app—the next time it’s pulled from the iOS application, you’ve already got that update. They’re basically saying that you get the sync engine for free.
DF: I think that the developer version is free now, but if you get into the professional version for enterprises, it’s actually one of the things that they charge for (I’m looking at the pricing page right now). I know that if you’re doing the developer version, you can get all the syncing for free, and so forth. But again, if you’re paying for that product, it’s simple.
I’m very against the NIH (Not Invented Here) syndrome. People are like, “We’re going to write our own status page and analytics engine!” Then you start adding it up, and say, “You’re gonna spend $150-200K writing this, and waste six months of time that’s not spent pushing your business forward. Why don’t you just spend a little bit of money on this other product that does it for you, so you can focus on fixing problems for the customers, not fixing technology problems?”
KG: The strongest skill a software engineer can have is to know when to make it someone else’s problem.
DF: That’s the biggest truth, and it can’t be said any better. I have no words other than what you said. So hands down to Kaushik.
If folks wanted to get started with Realm…I already know how to use it, and it’s become second nature to me. It’s still kind of fresh to you, though, so if you were to recommend how to get started with Realm, what would you tell someone to do?
KG: Almost all of the content that I need, I get from two places. One is obviously the documentation. It’s not as detailed, but it’s a very good place to start to get a feel for what things are.
But an even better recommendation (this is my standard go-to now) is your Caster.io course on this. Donn did not prod me to say that, by the way. He just genuinely asked this question, but this is what I really do. When I started out with Realm, I was like, “This Realm thing sounds interesting. I’ve talked to Christian, and I understand the theory. But what does it feel like? I want to get a better understanding of what it is.” So I made myself a hot cup of coffee, opened up Caster.io, and saw that Donn used Realm. So I thought, “I’ll see how things are.” When you see the code, you get a good feeling of how things work. That’s basically what I did, because I didn’t want to invest that much time in it if it wasn’t something that looked appealing to me.
Any video course would work, but Caster.io works pretty well. So, I opened Caster.io, looked at the courses, got an understanding of how it works, and thought, “Okay, this looks like something I can get behind.” Then I went to the documentation. As you implement, you’ll run into these small quirks, like we mentioned over the course of this episode. Usually, you’ll find them either in the documentation or the GitHub issues, so that was my strategy. What are your thoughts?
DF: I agree. I usually tell people to just go to https://realm.io. Here’s the key thing: today, we’re mainly focused on Realm Java, because this is an Android podcast. But they also support Objective-C and Swift. There’s starting to be quite a few folks listening to this show from the React Native crowd, and there’s a React Native binding too. And if you’re on the .NET side of things, doing cross-platform development, they have Xamarin bindings as well. So go the website, choose your platform, read the docs, and start hacking away. That’s usually what I recommend. If you need any help, you can always check out the Caster.io course.
I hope that this conversation has answered a lot of your questions about what we think about Realm in general, without having someone directly on the Realm team here to talk about it. These are our actual real thoughts. I have apps in production with Realm, and have had some for close to year now. It’s been performing well. Kaushik, you’re starting to use it now, and I have a bunch of other friends who already use it.
Hopefully, this answers your questions. If it didn’t, please hit us up on Twitter, and we’ll try to help you out.
KG: If there are things we mentioned offhand that you would like more clarification on, feel free to hit us up on Twitter, and we’ll make sure to give you the details, the context, or the specifics on the stuff that we mentioned.
If folks wanted to reach out to you and find out more about Realm’s specifics, what would be a good way to do that?
DF: They can reach me at @donnfelker on Twitter. What about if they want to ask you about the weird nuances of Realm?
KG: Please do! Do it on Twitter at @kaushikgopal. Seriously, folks, we’re very interested, and this is a big part of the learning process for us, too. If you keep asking quirky questions or the things that come up that seem like a no-go for you, this is how we’re fortunate enough to be able to find answers, so that we can jointly learn in the process.
DF: We’ve had many folks ask us specifically about our thought process on the the MVP pattern (and other MV* patterns), so we have another one of these conversations planned for those type of things. Please ask your questions, and we’ll get them in here!