Didn't get a chance to attend the Realm Sync in use - building and architecting a Mobile Chat App Meetup? Don't worry, we recorded the session and you can now watch it at your leisure to get you caught up.
Realm Sync in Use - Building and Architecting a Mobile Chat App
In this meetup, Andrew Morgan, a Staff Engineer at MongoDB, will walk you through the thinking, architecture and design patterns used in building a Mobile Chat App on iOS using MongoDB Realm Sync. The Chat app is used as an example, but the principles can be applied to any mobile app where sync is required. Andrew will focus on the data architecture, both the schema and the partitioning strategy used and after this session, you will come away with the knowledge needed to design an efficient, performant, and robust data architecture for your own mobile app.
- In this 70-minute recording, in the first 50 minutes or so, Andrew covers:
- Demo of the RChat App
- System/Network Architecture
- Data Modelling & Partitioning
- The Code - Integrating synced Realms in your SwiftUI App
And then we have about 20 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.
Shane McAllister: Hello, and welcome to the meetup. And we're really, really delighted that you could all join us here and we're giving people time to get on board. And so we have enough of a quorum of people here that we can get started. So first things first introductions. My name is Shane McAllister, and I'm a lead on the Developer Advocacy team here, particularly for Realm. And I'm joined today as, and we'll do introductions later by Andrew Morgan as well. Who's a staff engineer on the Developer Advocacy team, along with me. So, today we're doing this meetup, but it's the start of a series of meetups that we're doing, particularly in COVID, where everything's gone online. We understand that there's lots of events and lots of time pressures for people. We want to reach our core developer audience as easily as possible. So this is only our second meetup using our new live platform that we have.
Shane McAllister: And so very much thank you for coming. Thank you for registering and thank you for being here. But if you have registered and you certainly joined the Realm Global Community, it means that you will get notified of these future events instantly via email, as soon as we add them. So we have four more of these events coming over the next four weeks, four to six weeks as well too. And we'll discuss those a little bit at the end of the presentation. With regards to this platform, you're used to online platforms, this is a little bit different. We have chat over on the right-hand side of your window. Please use that throughout.
Shane McAllister: I will be monitoring that while Andrew is presenting and I will be trying to answer as much as I can in there, but we will using that as a function to go and do our Q&A at the end. And indeed, if you are up to it, we'd more than welcome to have you turn on your camera, turn on your mic and join us for that Q&A at the end of our sessions as well too. So just maybe mute your microphones if they're not already muted. We'll take care of that at the end. We'll open it out and everyone can get involved as well, too. So without further ado, let's get started. I'm delighted to hand over to Andrew Morgan.
Andrew Morgan: I'm going to talk through how you actually build a mobile app using Realm and in particular MongoDB Realm Sync. To make it a bit less dry. We're going to use an example app, which is called RChat, which is a very simple chat application. And if you like, it's a very simple version of WhatsApp or Slack. So the app is built for iOS using SwiftUI, but if you're building on Android, a lot of what I'm going to cover is still going to apply. So all the things about the data modeling the partitioning strategy, setting up the back end, when you should open and close Realms, et cetera, they're the same. Looking at the agenda. We're going to start off with a very quick demo of the app itself. So you understand what we're looking at. When we look at the code and the data model, we'll look at the components to make it up both the front end and the back end.
Andrew Morgan: One of the most important is how we came up with the data model and the partitioning strategy. So how partitioning works with Realm Sync? Why we use it? And how you actually come up with a strategy that's going to work for your app? Then we'll get to the code itself, both the front end iOS code, but also some stored procedures or triggers that we're running in the back end to stitch all of the data together. And finally, I promise I'll keep some time at the end so we can have a bit of interactive Q&A. Okay, so let's start with the demo.
Andrew Morgan: And so what we've got is a fairly simplistic chat app along the lines of WhatsApp or Slack, where you have the concept of users, chat rooms, and then that messages within those. So we've got three devices connected. You can register new users through the app, but using that radio button at the bottom, but for this, I'm going to use what I've created earlier. This will now authenticate the user with Realm back end. And you may notice as Rod came online, the presence updated in the other apps. So for example, in the buds here, you can see that the first two members are online currently, the middle one is the one I just logged in. And then the third one is still offline. You'll see later, but all of these interactions and what you're seeing there, they're all being implemented through the data changing. So we're not sending any rest messages around like that to say that this user is logged in, it's just the data behind the scenes changes and that gets reflected in the UI. And so we can create a new chat room.
Andrew Morgan: You can search for users, we've only got a handful here, so I'll just add Zippy and Jane. And then as I save, you should see that chat appear in their windows too. See they're now part of this group. And we can go in here and can send those messages and it will update the status. And then obviously you can go into there and people can send messages back. Okay. So it's a chat app, as you'd expect you can also do things like attach photos. Apologies, this is the Xcode beta the simulator is a little bit laggy on this. And then you can also share your location. So we'll do the usual kind of things you'd want to do in a chat room and then dive into the maps, et cetera. Okay. So with that, I think I can switch back to the slides and take a very quick look at what's going on behind the scenes from an architectural perspective.
Andrew Morgan: Let me get a pointer. So, this is the chat app you were seeing, the only time and so we've got the chat app, we've got the Realm embedded mobile database. We've got MongoDB Realm, which is the back end service. And then we've got MongoDB Atlas, which is the back end data store. So the only time the application interacts directly with Realm, the Realm service in the back end is when the users logging in or logging out or you're registering another user, the rest of the time, the application is just interacting with the local Realm database. And then that Realm database is synchronizing via the Realm service with other instances of the application. So for example, when I sent a message to Rod that just adds a chat message to the Realm database that synchronizes via MongoDB Realm Sync, and then that same day to get sent to the other Realm database, as well as a copy gets written to Atlas.
Andrew Morgan: So it's all data-driven. What we could do, which we haven't done yet is that same synchronization can also synchronize with Android applications. And also because the data is stored in Atlas, you can get at that same data through a web application, for example. So you only have to write your back end once and then all of these different platforms, your application can be put into those and work as it is.
Andrew Morgan: So the data model and partitioning. So first of all, Shane and I were laughing at this diagram earlier, trying to figure out how many years this picture has been in used by the Realm Team.
Shane McAllister: It's one of the evergreen ones I think Andrew. I think nobody wants to redesign it just yet. So we do apologize for the clip art nature of this slide.
Andrew Morgan: Yeah. Right. So, the big database cylinder, you can see here is MongoDB Atlas. And within there you have collections. If you're new to MongoDB, then a collection is analogous to a table in a relational database. And so in our shapes database, we've got collections for circles, stars, and triangles. And then each of those shapes within those collections, they've got an attribute called color. And what we've decided to do in this case is to use the color attribute as our partitioning key.
Andrew Morgan: So what that means is that every one of these collections, if they're going to be synced, they have to have a key called color. And when someone connects their Realm database and saying, they want to sync, they get to specify the value for the partitioning key. So for example, the top one specified that they want all of the blue objects. And so that means that they get, regardless of which collection they're in, they get all of the blue shapes. And so you don't have any control over whether you just synced the stars or just the triangles. You get all of the shapes because the partition is set to just the color. The other limitation or feature of this is that you don't get to choose that certain parts of the circle gets synchronized, but others don't. So it's all or nothing. You're either syncing all of the red objects in their entirety or you're not sinking the red objects in their entirety.
Andrew Morgan: So, why do we do this partitioning rather than just syncing everything to the mobile Realm database? One reason is space. You've obviously got constraints on how much storage you've got in your mobile device. And if, for example, you partitioned on user and you had a million users, you don't want every user's device to have to store data, all of those million users. And so you use the partitioning key to limit how much storage and network you're using for each of those devices. And the other important aspect is security. I don't necessarily want every other user to be able to see everything that's in my objects. And so this way you can control it, that on the server side, you make sure that when someone's logged in that they can only synchronize the objects that they're entitled to see. So, that's the abstract idea.
Andrew Morgan: Let's get back to our chat application use case, and we've got three top-level objects that we want to synchronize. The first one is the User. And so if I'm logged in as me, I want to be able to see all of the data. And I also want to be able to update it. So this data will include things like my avatarImage. It will include my userName. It will include a list of the conversations or the chat rooms that I'm currently a member of. So no one else needs to see all of that data. There's some of that data that I would like other people to be able to at least see, say, for example, my displayName and my avatarImage. I'd like other people to be able to see that. But if you think back to how the partitioning works and that it's all or nothing, I can either sync the entire User object or none of it at all.
Andrew Morgan: So what we have is, we have another representation of the User, which is called the Chatster. And it's basically a mirror of a subset of the data from the User. So it does include my avatar, for example, it does include my displayName. But what it won't include is things like the complete list of all of the chat rooms that I'm a member of, because other people have no business knowing that. And so for this one, I want the syncing rule to be that, anyone can read that data, but no one can update it.
Andrew Morgan: And then finally, we've got the ChatMessages themselves, and this has got a different access rules again, because we want all of the members within a chat room to be able to read them and also write new ones. And so we've got three top-level objects and they all have different access rules. But remember we can only have a single partitioning key. And that partitioning key has to be either a String, an objectID or a Long. And so to be able to get a bit more sophisticated in what we synchronized to which users, we actually cheat a little and instead, so a partitioning key, it's an attribute that we call partition. And within that, we basically have key value pairs. So for each of those types of objects, we can use a different key and value.
Andrew Morgan: So, for example, for the user objects or the user collection, we use the String, user=, and then _id. So the _id is what uniquely identifies the object or the document within the collection. So this way we can have it, that the rules on the server side will say that this partition will only sync if the currently logged in user has got the _id that matches. For the Chatster it's a very simple rule. So we're effectively hard coding this to say, all-users equals all-the-users, but this could be anything. So this is just a string that if you see this the back ends knows that it can synchronize everything. And then for the ChatMessages the key is conversation and then the value is the conversation-id.
Andrew Morgan: I'll show you in code how that comes together. So this is what our data model looks like. As I said, we've got the three top-level objects. User, Chatster and ChatMessage. And if we zoom in you'll see that a User is actually, its got a bunch of attributes in the top-level object, but then it's got sub-objects, or when sorting MongoDB sub-documents. So it's got sub-objects. So, the users got a list of conversations. The conversation contains a list of members and a UserPreferences or their avatarImage, the displayName, and know that they do have an attribute called partition. And it's only the top level object that needs to have the partition attributes because everything else is a sub-object and it just gets dragged in.
Andrew Morgan: I would, we also have a UserPreference contains a Photo, which is a photo object. And then Chatster, which is our read-only publicly visible object. We've got the partition and every time we try and open a Realm for the Chatster objects, we just set it to the String, all-users equals all-the-users. So it's very similar, but it's a subset of the data that I'm happy to share with everyone. And then finally we have the ChatMessage which again, you can see it's a top-level object, so it has to have the partition attribute.
Andrew Morgan: So how do we enforce that people or the application front end only tries to open Realms for the partitions that it's enabled that ought to? We can do that through the Realm UI in the back end. We do it by specifying a rule for read-only Realms and read-write Realms. And so in each case, all I'm doing here is I'm saying that I'm going to call a Realm function. And when that functions is called, it's going to be given passed a parameter, which is the partition that they're trying to access. And then that's just the name of the function.
Andrew Morgan: And I'm not going to go through this in great detail, but this is a simplified version of the canWritePartition. So this is what the sides, if the application is asking to open a Realm to make changes to it, this is how I check if they're allowed access to that partition. So the first thing we do is we take the partition, which remember is that Key Value string. And we split it to get the Key and the Value for that Key. Then we just do a switch based on what has been used as the Key. If it's the "user" then we check that the partitionValue matches the _id of the currently logged in user. And so that'll return true or false. The conversation is the most complex one. And for that one, it actually goes and reads the userDoc for this "user" and then checks whether this conversation.id is one that "user" is a member of. So, that's the most complex one. And then all users, so this is remember for the Chatster object, that always returns false, because the application is never allowed to make changes to those objects.
Andrew Morgan: So now we're looking at some of the Swift code, and this is the first of the classes that the Realm mobile database is using. So this is the top-level class for the Chatster object. The main things to note in here is so we're importing RealmSwift, which is the Realm Cocoa SDK. The Chatser it conforms to the object protocol, and that's actually RealmSwift.object. So, that's telling Realm that this is a class where the objects can be managed by the Realm mobile database. And for anyone who's used a SwiftUI, ObjectKeyIdentifiable protocol that's taking the place of identifiable. So it just gives each of these objects... But it means that Realm would automatically give each of these objects, an _id that can be used by Swift UI when it's rendering views.
Andrew Morgan: And then the other thing to notice is for the partition, we're hard coding it to always be all-users equals all-the-users, because remember everyone can read all Chatster objects, and then we set things up. We've got the photo objects, for example which is an EmbeddedObject. So all of these things in there. And for doing the sync, you also have to provide a primary key. So again, that's something that Realm insists on. If you're saying that you've implemented the object protocol, taking a look at one of the EmbeddedObjects instead of being object, you implement, you conform to the embedded object protocol. So, that means two things. It means that when you're synchronizing objects, this is just synchronized within a top-level object. And the other nice thing is, this is the way that we implement cascading deletes. So if you deleted a Chatster object, then it will automatically delete all of the embedded photo objects. So that, that makes things a lot simpler.
Andrew Morgan: And we'll look quickly at the other top-level objects. We've got the User class. We give ourselves just a reminder that when we're working with this, we should set the partition to user equals. And then the value of this _id field. And again, it's got userPreferences, which is an EmbeddedObject. Conversations are a little bit different because that's a List. So again, this is a Realm Cocoa List. So we could say RealmSwift.list here. So we've got a list of conversation objects. And then again, those conversation objects little displayName, unreadCount, and members is a List of members and so on and so on. And then finally just for the complete desk here, we've got the ChatMessage objects.
Andrew Morgan: Okay. So those of us with objects, but now we'll take a quick look at how you actually use the Realm Cocoa SDK from your Swift application code. As I said before the one interaction that the application has directly with the Realm back end is when you're logging in or logging out or registering a new user. And so that's what we're seeing here. So couple of things to note again, we are using Realm Cocoa we're using Combine, which for people not familiar with iOS development, it's the Swift event framework. So it's what you can use to have pipelines of operations where you're doing asynchronous work. So when I log in function, yes, the first thing we do is we actually create an instance of our Realm App. So this id that's something that you get from the Realm UI when you create your application.
Andrew Morgan: So that's just telling the front end application what back end application it's connecting to. So we can connect to Realm, we then log in, in this case, we're using email or username, password authentication. There's also anonymous, or you can use Java UTs there as well. So once this is successfully logged the user in, then if everything's been successful, then we actually send an event to a loginPublisher. So, that means that another, elsewhere we can listen to that Publisher. And when we're told someone's logged in, we can take on other actions. So what we're doing here is we're sending in the parameter that was passed into this stage, which in this case is going to be the user that's just logged in.
Andrew Morgan: Okay, and I just take a break now, because there's two ways or two main ways that you can open a Realm. And this is the existing way that up until the start of this week, you'd have to use all of the time, but it's, I've included here because it's still a useful way of doing it because this is still the way that you open Realm. If you're not doing it from within SwiftUI view. So this is the Publisher, we just saw the loginPublisher. So it receives the user. And when it receives the user it creates the configuration where it's setting up the partitionValue. So this is one that's going to match the partition attribute and we create the user equals, so a string with user equals and then the user.id.
Andrew Morgan: And then we use that to open a new Realm. And again, this is asynchronous. And so we send that Realm and it's been opened to this userRealmPublisher, which is yet another publisher that Combine will pass in that Realm once it's available. And then in here we store a copy of the user. So, this is actually in our AppState. So we create a copy of the user that we can use within the application. And that's actually the first user. So, when we created that Realm, it's on the users because we use the partition key that only matches a single user. There's only actually going to be one user object in this Realm. So we just say .first to receive that.
Andrew Morgan: Then, because we want to store this, and this is an object that's being managed by Realm. We create a Realm, transaction and store, update the user object to say that this user is now online. And so when I logged in, that's what made the little icon turn from red to green. It's the fact that I updated this, which is then synchronized back to the Realm back end and reflected in all the other Realm databases that are syncing.
Andrew Morgan: Okay. So there is now also asynchronous mode of opening it, that was how we had to open it all the way through our Swift code previously. But as of late on Monday, we actually have a new way of doing it. And I'm going to show you that here, which is a much more Swift UI friendly way of doing it. So, anyone who went to Jason's session a couple of weeks ago. This is using the functionality that he was describing there. Although if you're very observant, you may know that some of the names have been changed. So the syntax isn't exactly the same as you just described. So let's give a generic example of how you'd use this apologies to people who may be not familiar with Swift or Swift UI, but these are Swift UI views.
Andrew Morgan: So within our view, we're going to call a ChildView. So it's a sub view. And in there we pass through the environment, a realmConfiguration, and that configuration is going to be based on a partition. So we're going to give a string in this case, which is going to be the partition that we want to open, and then synchronize. In this case, the ChildView doesn't do anything interesting. All it does is called the GrandChildView, but it's important to note that how the environments work with Swift UI is they automatically get passed down the view hierarchy. So even though we're not actually passing into the environment for GrandChildView, it is inheriting it automatically.
Andrew Morgan: So within GrandChildView, we have an annotation so observed results. And what we're doing here is saying for the Realm that's been passed in, I want items to represent the results for all items. So item is a class. So all objects of the class item that are stored in those results. I want to store those as the items results set, and also I'm able to get to the Realm itself, and then we can pass those into, so we can iterate over all of those items and then call the NameView for each of those items. And it's been a long way getting to here, but this is where we can finally actually start using that item. So when we called NameView, we passed in the instance of item and we use this Realm annotation to say that it's an ObservedRealmObject when it's received in the NameView, and why that's important is it means that we don't have to explicitly open Realm transactions when we're working with that item in this View.
Andrew Morgan: So the TextField View, it takes the label, which is just the label of the TextField and binding to the $items.name. So it takes a binding to a string. So, TextField can actually update the data. It's not just displaying it, it lets the user input stuff. And so we can pass in a binding to our item. And so text fields can now update that without worrying about having to explicitly open transactions.
Andrew Morgan: So let's turn to our actual chat application. And so a top-level view is ContentView, and we do different things depending on whether you're logged in yet, but if you are logged in yet, then we call the ConversationListView, and we pass in a realmConfiguration where the partition is set user equals and then the _id of the user. Then within the ConversationListView, which is represents what you see from the part of the application here. We've got a couple of things. The first is, so what have we got here? Yeah. So we do some stuff with the data. So, we display each of these cards for the conversations, with a bit I wanted to highlight is that when someone clicks on one of these cards, it actually follows a link to a ChatRoomView. And again, with the ChatRoomView, we pass in the configuration to say that it's this particular partition that we want to open a Realm for.
Andrew Morgan: And so once we're in there we, we get a copy of the userRealm and the reason we need a copy of the userRealm is because we're going to explicitly upgrade, update the unreadCount. We're going to set it to zero. So when we opened the conversation, we'll mark all of the messages as read. And because we're doing this explicitly rather than doing it via View, we do still need to do the transaction here. So that's why we received that. And then for each of these, so each of these is a ChatRoomBubble. So because we needed the userRealm in this View, we couldn't inject the ChatMessage View or the ChatMessage Realm into here. And so instead, rather than working with the ChatMessages in here, we have to pass, we have to have another subview where that subview is really just there to be able to pass in another partitionValue. So in this case, we're passing in the conversation equals then the id of the conversation.
Andrew Morgan: And so that means that in our ChatRoomBubblesView, we're actually going to receive all of the objects of type ChatMessage, which are in that partition. And the other thing we're doing differently in here is that when we get those results, we can also do things like sorting on them, which we do here. Or you can also add a filter on here, if you don't want this view to work with every single one of those chatMessages, but in our case, all of those chatMessages for this particular conversation. And so we do want to work with all of them, but for example, you could have a filter here that says, don't include any messages that are more older than five months, for example. And then we can loop over those chatMessages, pass them to the ChatBubbleView which is one of these.
Andrew Morgan: And the other thing we can do is you can actually observe those results. So when another user has a chatMessage, that will automatically appear in here because this result set automatically gets updated by Realm Sync. So the back end changes, there's another chatMessage it'll be added to that partition. So it appears in this Realm results set. And so this list will automatically be updated. So we don't have to do anything to make that happen. But what I do want to do is I want to scroll to the bottom of the list of these messages when that happens. So I explicitly set a NotificationToken to observe those chatMessages. And so whenever it changes, I just scroll to the bottom.
Andrew Morgan: Then the other thing I can do from this view is, I can send new messages. And so when I do that, I just create, we received a new chatMessage and I just make sure that check a couple of things. Very importantly, I set the conversation id to the current conversation. So the chatMessages tag to say, it's part of this particular conversation. And then I just need to append it to those same results. So, that's the chats results set that we had at the top. So by appending it to that List, Realm will automatically update the local Realm database, which automatically synchronizes with the back end.
Andrew Morgan: And all it's really doing here is, it is creating a new userDoc based on the user.id that just logged in, set stuff up in there and including setting that they're offline and that they've got no conversations. And then it inserts that into the userCollection. So now we have a userDoc in the userCollection and that user they'll also receive that user object in their application because they straight away after logging in, they opened up that user Realm. And so they'll now have a copy of their own userDoc.
Andrew Morgan: Then we've got a couple of database Triggers. So this one is hit every time a new chatMessage is added. And when that happens, that function will search for all of the users that have that conversation id in their list of conversations. And then it will increment the unreadCount value for that particular conversation within that particular user's document. And then finally, we've got the one that creates the Chatster document. So whenever a user document is created or updated, then this function will run and it will update the Chatser document. So it also always provides that read-only copy of a subset of the data. And the other thing that it does is that when a conversation has been added to a particular user, this function will go and update all of the other users that are part of that conversation. So that those user documents also reflect the fact that they're a part of that document.
Andrew Morgan: Okay. Um, so that was all the material I was going to go through. We've got a bunch of links here. So the application itself is available within the Realm Organization on GitHub. So that includes the back end Realm application as well as the iOS app. And it will also include the Android app once we've written it. And then there's the Realm Cocoa SDK the docs, et cetera. And if you do want to know more about implementing that application, then there's a blog post you can read. So that's the end-to-end instructions one, but that blog also refers to one that focuses specifically on the data model and partitioning. And then as Shane said, we've got the community forums, which we'd hope everyone would sign up for.
Shane McAllister: Super. Thank you, Andrew. I mean, it's amazing to see that in essence, this is something that, WhatsApp, entire companies are building, and we're able to put it demo app to show how it works under the hood. So really, really appreciate that. There are some questions Andrew, in the Q&A channel. There's some interesting conversations there. I'm going to take them, I suppose, as they came in. I'll talk through these unless anybody wants to open their mics and have a chat. There's been good, interesting conversations there. We'd go back to, I suppose, the first ones was that in essence, Richard brought this one up about presence. So you had a presence status icon there on the members of the chat, and how did that work was that that the user was logged in and the devices online or that the user is available? How were you managing that Andrew?
Andrew Morgan: Yeah. So, how it works is when a user logs in, we set it that that user is online. And so that will update the user document. That will then get synchronized through Realm Sync to the back end. And when it's received by the back end, it'll be written to the Atlas database and the database trigger will run. And so that database trigger will then replicate that present state to the users Chatser document. And then now going in the other direction, now that documents has changed in Atlas, Realm Sync will push that change to every instance of the application. And so the Swift UI and Realm code, when Realm is updated in the mobile app, that will automatically update the UI. And that's one of the beauties of working with Swift UI and Realm Cocoa is when you update the data model in the local Realm database, that will automatically get reflected in the UI.
Andrew Morgan: So you don't have to have any event code saying that, "When you receive this message or when you see this data change, make this change the UI" It happens automatically because the Realm objects really live within the application and because of the clever work that's been done in the Realm Cocoa SDK, when those changes are applied to the local copy of the data, it also notifies Swift UI that the views have to be updated to reflect the change. And then in terms of when you go offline if you explicitly log out it will set it to offline and you get the same process going through again. If you stay on, if you stay logged in, but you've had the app in the background for eight hours, or you can actually configure how long, then you'll get a notification saying, "Do you want to continue to stay, remain logged in Or do you want to log out?"
Andrew Morgan: The bit I haven't added, which would be needed in production is that when you force quit or the app crashes, then before you shut things down, just go and update the present state. And then the other presence thing you could do as well is in the back end, you could have a schedule trigger so that if someone has silently died somewhere if they've been online rate hours or something, you just mark them to show their offline.
Shane McAllister: Yeah. I think, I mean, presence is important, but I think the key thing for me is that, how much Realm does under the hood on behalf of you [inaudible 00:43:14] jumping on a little bit.
Andrew Morgan: With that particular one, I can do the demo. So for example, let's go on this window. You can see that, so this is Zippy is the puppet. So if you monitor Zippy, then I'm in the this is actually, I'll move this over. Because I need to expand this a little.
Shane McAllister: I have to point out. So Andrew's is in Maidenhead in England, this demo for those of you not familiar, there was a children TV program, sometime in the late '70s early '80s [inaudible 00:43:54]. So these are the characters from this TV program where they all the members in this chat app.
Andrew Morgan: Yeah. And I think in real life, there's actually a bit of a love triangle between three of them as well.
Shane McAllister: We won't go there. We won't go there.
Andrew Morgan: So, yeah. So, this is the data that's stored in, so I'll zoom in a little bit. This is the data that's stored in Atlas in the back end. And so if I manually go in and so you want to monitor Zippy status in the iPhone app, if I change that present state in the back end, then we should see thatZippy goes offline. So, again there's no code in there at all. All I've had to do is buying that present state into the Swift UI view.
Shane McAllister: That's a really good example. I think that be any stronger examples on doing something in the back end and it immediately reflect in the UI. I think it works really well. Kurt tied a question with regard to the partition Andrew. So, all the user I tried to run, this is a demo. We don't have a lot with users. In essence, If this was a real app, we could have 10 million user objects. How would we manage that? How would we go about that?
Andrew Morgan: Yeah. So, the reason I've got all, the reason I've done it like it's literally all users is because I want you to be able to search. I want you to be able to create a new chat room from the mobile app and be able to search through all of the users that are registered in the system. So that's another reason why we don't want the Chatster object to contain everything about user, because he wants it to be fairly compact so that it doesn't matter if you are storing a million of them. So ideally we just have the userName and the avatar in there. If you want you to go a step further, we could have another Chatser object with just the username. And also if it really did get to the stage where you've got hundreds of millions or something, or maybe for example in a Slack type environment where you want to have organizations that instead of having the user, instead of being all the users, you could actually have the old equals orgName as your partition key.
Andrew Morgan: So you could just synchronize your organization rather than absolutely everything. If there really was too many users that you didn't want them all in the front end, at that point, you'd start having to involve the back end when you wanted to add a new user to a chat room. And so you could call a Realm function, for example, do a query on the database to get that information.
Shane McAllister: Sure. Yeah, that makes sense. Okay, in terms of the chat that I was, this is our demo, we couldn't take care of it on a scale. In essence, these are the things that you would have to think about if you were paying to do something for yourself in this area. The other thing that Andrew was, you showed the very start you're using embedded data for that at the moment in the app. Is another way that we did in our coffee shop as well.
Andrew Morgan: Sorry. There was a bit of an echo because I think when I have my mic on and you're talking, I will mute it.
Shane McAllister: I'll repeat the question. So it was actually Richard who raised this was regarding the photos shared in the chat, Andrew, they shared within embedded data, as opposed to say how we did it in our oafish open source app with an Amazon S3, routine essentially that ran a trigger that ran in the background and we essentially passed the picture and just presented back a URL with the thumbnail.
Andrew Morgan: Yeah. In this one, I was again being a little bit lazy and we're actually storing the binary images within the objects and the documents. And so what we did with the oafish application is we had it that the original document was the original photo was uploaded to S3 and we replace it with an S3 link in the documents instead. And you can do that again through a Realm trigger. So every time a new photo document was added you could then so sorry, in this case it would be a subdocument within the ChatMessage, for example, then yeah. The Realm trigger. When you receive a new ChatMessage, it could go and upload that image to S3 and then just replace it with the URL.
Andrew Morgan: And to be honest that's why in the photo, I actually have the, I have a thumbnail as well as the full size image, because the idea is that the full-size image you move that to S3 and replace it with a link, but it can be handy to have the thumbnails so that you can still see those images when you're offline, because obviously for the front end application, if it's offline, then an S3 link isn't much use to you. You can't go and fetch it. So by having the thumbnail, as well as the full-size image, you've got that option of effectively archiving one, but not the thumbnail.
Shane McAllister: Perfect. Yeah. That makes a lot of sense. On a similar vein about being logged in, et cetera, as well, to curtail the question with regard, but if there's a user Realm that is open as long as you're logged in, and then you pass in an environment Realm partition, are they both open in that view?
Andrew Morgan: No, I think it'll still be, I believe it'll be one. Oh, yes. So both Realms. So if you, for example open to use a Realm and the chats to Realm then yes. Both of those Realms would be open simultaneously.
Shane McAllister: Okay. Okay, perfect. And I'm coming through and fairplay to Ian and for writing this. This is a long, long question. So, I do appreciate the time and effort and I hope I catch everything. And Ian, if you want me to open your mic and chime in to ask this question, by all means as well, that just let me know in the chat I'll happily do. So perhaps Andrew, you might scroll back up there in the question as well, too. So it was regarding fetching one object type across many partitions, many partition keys, actually. So, Ian he had a reminder list each shared with a different person, all the reminders in each list have a partition key that's unique for that chair and he wants to show the top-level of that. So we're just wondering how we would go about that or putting you on the spot here now, Andrew. But how would we manage that? Nope, you're muted again because of my feedback. Apologies.
Andrew Morgan: Okay. So yeah, I think that's another case where, so there is the functionality slash limitation that when you open a Realm, you can only open it specifying a single value for the partition key. And so if you wanted to display a docket objects from 50 different partitions, then the brute-force way is you have to go and to open 50 Realms, sort of each with a different partition id, but that's another example where you may make a compromise on the back end and decide you want to duplicate some data. And so in the similar way to, we have the Chatster objects that are visible all in a single partition, you could also have a partition, which just contains the list of list.
Andrew Morgan: So, you could, whenever someone creates a new list object, you could go and add that to another document that has the complete list of all of the lists. But, but yeah, this is why when you're using Realms Sync, figuring out your data model and your partitioning strategy is one of the first things, as soon as you've figured out the customer store for what you want the app to do. The next thing you want to do is figure out the data model and your partitioning strategy, because it will make a big difference in terms of how much storage you use and how performance is going to be.
Shane McAllister: So Ian, your mic is open to chime in on this. Or did we cover? You good? Maybe it's not open, this is the joy.
Ian: Do you hear me now?
Shane McAllister: Yes. MongoDB.
Ian: Yeah. I need to go think about the answer. So you, because I was used to using Realm before Realm Sync, so you didn't have any sharing, but you could fetch all the reminders that you wanted, whatever from any lists and just show them in a big list. I need to go think about the answer. How about [inaudible 00:54:06].
Andrew Morgan: Yeah, actually there's a third option that I didn't mention is Realm has functions. So, the triggers we looked at that actually implemented as Realm functions, which they're very simple, very lightweight equivalent to the AWS Lambda functions. And you can invoke those from the front end application. So if you wanted to, you could have a function which queries the Atlas database to get a list of all of the lists. And so then it would be a single call from the front end application to a function that runs in the back end. And then that function could then go and fetch whatever data you wanted from the database and send it back as a result.
Ian: But that wouldn't work if you're trying to be an offline first, for example.
Andrew Morgan: Yeah. Sort of that, that relies on online functionality, which is why is this, I always try and do it based on the data in Realm as much as possible, just because of that. That's the only way you get the offline first functionality. Yeah.
Ian: Cool. I just think about it. Thank you.
Shane McAllister: Perfect. Thank you Ian. And was there any other followups Ian?
Andrew Morgan: Actually, there's one more hack I just thought of. You can add, so you can only have a single partition key for a given Realm app, but I don't think there's any reason why you couldn't have multi, so you can have multiple Realm apps accessing the same Atlas database. And so if you could have the front end app actually open multiple Realm apps, then each of those Realm apps could use a different attribute for partitioning.
Shane McAllister: Great. Lets-
Andrew Morgan: So it's a bit hacky but that might work.
Shane McAllister: No worries. I'm throwing the floor open to Richard's if you're up to it, Richard, I enabled hosts for you. You had a number of questions there. Richard from [inaudible 00:56:19] is a longtime friend on Realm. Do you want to jump on Richard and go through those yourself or will I vocalize them for you? Oh, you're you're still muted, Richard.
Richard: Okay. Can you hear me now?
Shane McAllister: We can in deed.
Richard: Okay. I think you answered the question of very well about the image stuff. We've actually been playing around with the Amazon S3 snippets and it's a great way of, because often we need URLs for images and then the other big problem with storing images directly is you're limited to four megabytes, which seems to be the limit for any data object right on Realm. So but Andrew had a great pointer, which is to store your avatars because then you can get them in offline mode. That's actually been a problem with using Amazon S3. But what was the other questions I had, so are you guys going to deprecate the asyncOpen? Because, we've noticed some problems with it lately?
Andrew Morgan: Not, that I'm aware of.
Andrew Morgan: It's because, I think there's still use cases for it. So, for example because when a user logs in, I'm updating their presence outside of a view, so it doesn't inherit the Realm Cocoa magic that's going on when integrated with Swift UI. And so I still have that use case, and now I'm going to chat with, and there may be a way around it. And as I say, the stuff only went, the new version of Realm Cocoa only went live late on Monday.
Andrew Morgan: So I've updated most things, but that's the one thing where I still needed to use the asyncOpen. When things have quietened down, I need Jason to have a chat with him to see if there's an alternate way of doing it. So I don't think asyncOpen is going away as far as I know. Partly of course, because not everyone uses Swift UI. We have to have options for UI kit as well.
Richard: Yeah. Well, I think everybody's starting to move there because Apple's just pushing. Well, the one last thing I was going to say about presence before I was a Realm programmer in that that was three years ago. I actually adopted Realm Sync very early. When it just came out in 2017, I was a Firebase programmer for about three years. And one thing Firebase had is the one thing they did handle well, was this presence idea, because you could basically say you could attach yourself to like a Boolean in the database and say, as long as I'm present, that thing says true, but the minute I disconnect, it goes false. And then the other people could read that they could say always connected or is not connected. And I can implement that with a set of timers that the client says on present, I'm present every 30 seconds, that timer updates.
Richard: And then there's a back end service function that clears a flag, but it's a little bit hacky. It would be nice if in Realm, there was something where you could say attach yourself to an object and then Realm would automatically if the device wasn't present, which I think you could detect pretty easily, then it would just change state from true to false. And then the other people could see that it was, that device had actually gone offline. So, I don't know if that's something you guys are thinking of in future release.
Andrew Morgan: Yeah. I'm just checking in the triggers, exactly what we can trigger on.
Richard: Because somebody might be logged in, but it doesn't mean that you're necessarily, they are the other end.
Andrew Morgan: Yeah. So, what you can do, so someone on the device side, one thing I was hoping to do, but I hadn't had a chance to is, so you can tell when the application is minimized. So, at the moment we're going to use minimize as their app. They get a reminder in X hours saying, you sure you still want to remain logged in. But that could automatically, instead of asking them it could just go and update their status to say I might. So, you can do it, but there's, I'm not aware of anything that for example, Realms realizing that the session has timed out. And so it.
Richard: I personally could get on an airplane and then flight attendants could say, okay, put everything in airplane mode. So you just do that. And then all of a sudden you're out, doesn't have time to go. If you make it, if you put the burden on the app, then there's a lot of scenarios where you're not going to, the server is going to think it's connected.
Andrew Morgan: I think it's every 30 minutes, the user token is refreshed between the front end of the back end. So yeah. We could hook something into that to say that, the back end could say that if this user hasn't refreshed their token in 31 minutes, then they're actually offline.
Richard: Yeah. But it'd be nice while at Firebase, you could tell within, I remember time yet, it was like three minutes. It would eventually signal, okay, this guy's not here anymore after he turned off the iPhone.
Andrew Morgan: Yeah, that's the thing going on.
Richard: Yeah, that was also my question.
Andrew Morgan: You couldn't implement that ping from the app. So like, even when it's in the background, you can have it wake up every five minutes and set call the Realm function and the Realm function just updates the last seen at.
Richard: Excellent. Well, that's what we're doing now, we're doing this weird and shake. Yeah, but this is a great, great demo of, it's a lot more compelling than task list. I think this should be your flagship demo. Not the test. I was hoping.
Andrew Morgan: Yeah. The thing I wrote before this was a task list, but I think the task list is a good hello world, but yes. But once you've done the hello world, you need to figure out how you do the tougher. So it's all the time.
Richard: Great. Yeah. About five months ago, I ended up writing a paper on medium about how to do a simple Realm chat. I called it simple Realm chat, which was just one chat thread you could log in and everybody could chat on the same thread. It was just but I was amazed that and this was about six months ago, you could write a chat app for Realm, which was no more than 150 lines of code, basically. But try and do that in any like XAMPP. It's like you'd be 5,000 lines of code before you got anything displayed. So Realm is really powerful that way. It's an amazing, you've got, you're sitting on the Rosetta Stone for communication and collaborative apps. This is I think one of the most seminal technologies in the world for that right now.
Shane McAllister: Thank you, Richard. We appreciate that. That's very-
Richard: You're commodifying. I mean, you're doing to collaboration with windows did to desktop programming like 20 years ago, but you've really solved that problem. Anyway, so that's, that's my two cents. I don't have any more questions.
Shane McAllister: Perfect. Thank you. No, thank you for your contribution. And then Kurt, you had a couple of questions on opened you up to come on and expose yourself here as well too. Hey Kurt, how are you?
Kurt: Hey, I'm good. Can you hear me?
Shane McAllister: We can in deed loud and clear.
Kurt: All right. Yeah. So this I've been digging into this stuff since Jason released this news, this new Realm Cocoa merge that happened on Monday, 10.6 I think is what it is, but so this .environment Realm. So you're basically saying with the ChatBubbles thing, inside this view, we're going to need this partition. So we're going to pass that in as .environment. And I'm wondering, and part of my misunderstanding, I think is because I came from old row and trying to make that work here. And so it opens that. So you go in into this conversation that has these ChatBubbles with this environment. And then when you leave, does that close that, do you have to open and close things or is everything handled inside that .environment?
Andrew Morgan: Everything should be handled in there that once in closing. So, top-level view that's been had that environment passed in, I think when that view is closed, then the the Realm should close instead.
Kurt: So, when you go back up and you no longer accessing the ChatBubblesView, that has the .environment appended to it, it's just going to close.
Andrew Morgan: Yeah. So let me switch to Share screen again. Yeah. So, for example, here, when I open up this chat room it's passed in the configuration for the ChatMessages Realm,.
Kurt: Right. Because, it's got the conversation id, showing the conversation equals that. And so, yeah.
Andrew Morgan: Yeah. So, I've just opened a Realm for that particular partition, when I go back that Realm-
Kurt: As soon as you hit chats, just the fact that it's not in the view anymore, it's going to go away.
Andrew Morgan: Yeah, exactly. And then I opened another chat room and it's open to another Realm for different partition.
Kurt: That's a lot of boilerplate code that's gone, but just like the observing and man that's really good. Okay. And then my only other question was, because I've gone over this quite a few times, you answered one of my questions on the forum with a link to this. So I've been going through it. So are you going to update the... You've been updating the code to go with this new version, so now you're going to go back and update the blog post to show all that stuff.
Andrew Morgan: Yeah. Yeah. So, the current plan is to write a new blog post. That explains what it takes to use this new to take advantage of the new features that are added on Monday. Because, ca the other stuff, it still works. There's nothing wrong with the other stuff. And if for example, you were using UI kits rather than Swift UI, there is probably more useful than the current version of the app. We may change our mind at some point, but the current thinking is, let's have a new post that explains how to go from the old world to the new world.
Kurt: Okay.Great. Well, looking forward to it.
Shane McAllister: Super Kurt, thanks so much for jumping in on that as well too. We do appreciate it. I don't think I've missed any questions in the general chat. Please shout up or drop in there if I haven't. But really do appreciate everybody's time. I know we're coming up on time here now and the key things for me to point out is that this is going to be regular. We want to try and connect with our developer community as much as possible, and this is a very simple and easy way to get that set up and to have part Q&A and jumping back in then to showing demos and how we're doing it and back out again, et cetera as well. So this has been very interactive, and we do appreciate that. I think the key thing for us is that you join and you'll probably have, because you're here already is the Realm global community, but please share that with any other developers and any other friends that you have looking to join and know what we're doing in Realm.
Shane McAllister: Our Twitter handle @realm, that's where we're answering a lot of questions in our community forums as well, too. So post any technical questions that you might have in there, both the advocacy team and more importantly, the realm engineering team and man those forums quite regularly as well, too. So, there's plenty to go there and thank you so much, Andrew, you've just put up the slide I was trying to get forward the next ones. So, coming up we have Nicola and talking about Realm.NET for Xamarin best practices and roadmap. And so that's next week. So, we're really are trying to do this quite regularly. And then in March, we've got Jason back again, talking about Realm Swift UI, once again on Property wrappers on the MVI architecture there as well too. And you have a second slide Andrew was there the next too.
Shane McAllister: So moving beyond then further into March, there's another Android talk Kotlin multi-platform for modern mobile apps, they're on the 24th and then on moving into April, but we will probably intersperse these with others. So just sign up for Realm global community on live.mongodb.com, and you will get emails as soon as we add any of these new media events. Above all, I firstly, I'd like to say, thank you for Andrew for all his hard work and most importantly, then thank you to all of you for your attendance. And don't forget to fill in the swag form. We will get some swag out to you shortly, obviously shipping during COVID, et cetera, takes a little longer. So please be patient with us if you can, as well too. So, thank you everybody. We very much appreciate it. Thank you, Andrew, and look out for more meetups and events in the global Realm community coming up.
Andrew Morgan: Thanks everyone.
Shane McAllister: Take care everyone. Thank you. Bye-bye.