Realm Meetup - SwiftUI Testing and Realm with Projections
Rate this article
Didn't get a chance to attend the SwiftUI Testing and Realm with Projections Meetup? Don't worry, we recorded the session and you can now watch it at your leisure to get you caught up.
In this meetup, Jason Flax, Lead iOS Engineer, makes a return to explain how the testing landscape has changed for iOS apps using the new SwiftUI framework. Learn how to write unit tests with SwiftUI apps powered by Realm, where to put your business logic with either ViewModels or in an app following powered by Model-View-Intent, and witness the power of Realm's new Projection feature.
In this 50-minute recording, Jason spends about 40 minutes presenting
- Testing Overview for iOS Apps
- What's Changed in Testing from UIKit to SwiftUI
- Unit Tests for Business Logic - ViewModels or MVI?
- Realm Projections - Live Realm Objects that Power your View
After this, we have about 10 minutes of live Q&A with Ian & Jason and our community . For those of you who prefer to read, below we have a full transcript of the meetup too.
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 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.
(As this is verbatim, please excuse any typos or punctuation errors!)
We also have MongoDB.live, this is taking place on July 13th and 14th. This is a free event and we have a whole track of talks that are dedicated to mobile and mobile development. So, you don't need to know anything about MongoDB as a server or anything like that. These talks will be purely focused on mobile development. So you can definitely join that and get some benefit if you're just a mobile developer. A little bit about housekeeping here. This is the Bevy platform. In a few slides, I'm going to turn it back over to Jason. Jason's going to run through a presentation. If you have any questions during the presentation, there's a little chat box in the window, so just put them in there. We have other team members that are part of the Realm team that can answer them for you. And then at the end, I'll run through them as well as part of a Q&A session that you can ask any questions.
Also, if you want to make this more interactive, we're happy to have you come on to the mic and do your camera and you can ask a question live as well. So, please get connected with us. You can join our forums.realm.io. Ask any questions that you might have. We also have our community get hubs where you can file an issue. Also, if you want to win some free swag, you can go on our Twitter and tweet about this event or upcoming events. We will be sending swag for users that tweet about us. And without further ado, I will stop sharing my screen and turn it over to Jason.
Jason: Hello, everyone. Hope everyone's doing well. I'm going to figure out how to share my screen with this contraption. Can people see my screen?
Ian: I can see it.
Jason: Cool stuff. All right. Is the thing full screen? Ian?
Ian: Sorry. I was muted, but I was raising my finger.
Jason: Yeah, I seem to always do these presentations when I'm visiting the States. I normally live in Dublin and so I'm out of my childhood bedroom right now. So, I don't have all of the tools I would normally have. For those of you that do not know me, my name is Jason Flax. I'm the lead engineer on the Realm Cocoa team. I've been at MongoDB for about five years, five years actually in three days, which is crazy. And we've been working with Realm for about two years now since the acquisition. And we've been trying to figure out how to better integrate Realm into SwiftUI, into all the new stuff coming out so that it's easier for people to use. We came up with a feature not too long ago to better integrate with the actual life cycle of SwiftUI ideas.
That's the ObservedRealmObject out observed results at state Realm object, the property rappers that hook into the view make things easy. We gave a presentation on the architectures that we want to see people using what SwiftUI, ruffled some feathers by saying that everybody should rewrite 50,000 lines of code and change the architecture that we see fit with SwiftUI. But a lot of people are mainly asking about testing. How do you test with SwiftUI? There aren't really good standards and practices out there yet. It's two years old. And to be honest, parts of it still feel a bit preview ish. So, what we want to do today is basically to go over, why do we test in the first place? How should you be testing with Realm?
How should you be testing the SwiftUI and Realm? What does that look like in a real world scenario? And what's coming next for Realm to better help you out in the future? Today's agenda. Why bother testing? We've all worked in places where testing doesn't happen. I encourage everybody to test their code. How to test a UI application? We're talking about iOS, macOS, TBOS, watchOS, they would be our primary users here. So we are testing a UI application. Unit and integration testing, what those are, how they differ, how Realm fits in? Testing your business logic with Realm. We at least internally have a pretty good idea of where we see business logic existing relative to the database where that sits between classes and whatnot. And then finally a sneak peek for Projections and a Q&A.
So, Projections is a standalone feature. I'll talk more about it later, but it should greatly assist in this case based on how we've seen people using Realm in SwiftUI. And for the Q&A part, we really want to hear from everybody in the audience. Testing, I wouldn't call it a hotly contested subject but it's something that we sometimes sit a bit too far removed from not building applications every day. So, it's really important that we get your feedback so that we can build better features or provide better guidance on how to better integrate Realm into your entire application life cycle. So why bother testing? Structural integrity, minimize bugs, prevent regressions, improve code quality and creating self-documenting code, though that turned to be dangerous as I have seen it used before, do not write documentation at all. I won't spend too long on this slide.
I can only assume that if you're here at the SwiftUI testing talk that you do enjoy the art of testing your code. But in general, it's going to create better code. I, myself and personal projects test my code. But I certainly didn't when I first started out as a software engineer, I was like, "Ah, sure. That's a simple function there. It'll never break." Lo and behold, three months later, I have no idea what that function is. I have no idea what it did, I was supposed to do and now I have a broken thing that I have to figure out how to fix and I'll spend an extra week on it. It's not fun for anybody involved. So, gesture code. How to test a UI application. That is a UI application. So, unit tests, unit tests are going to be your most basic test.
It tests functions that test simple bodies of work. They're going to be your smallest test, but you're going to a lot of them. And I promise that I'll try to go quickly through the basics testing for those that are more seasoned here. But unit tests are basically input and output. If I give you this, I expect this back in return. I expect the state of this to look like this based on these parameters. Probably, some of our most important tests, they keep the general structure of the code sound. Integration tests, these are going to, depending on the context of how you're talking about it could be integrating with the backend, it could be integrating with the database. Today, we're going to focus on the latter. But these are the tests that actually makes sure that some of the like external moving parts are working as you'd expect them to work.
Acceptance tests are kind of a looser version of integration tests. I won't really be going over them today. End-to-end tests, which can be considered what I was talking about earlier, hitting an actual backend. UI testing can be considered an end to end test if you want to have sort of a loose reasoning about it or UI testing that actually tests the back. And it basically does the whole system work? I have a method that sends this to the server and I get this back, does the thing I get back look correct and is the server state sound? And then smoke tests, these are your final tests where your manager comes to you at 8:00 PM on the day that you were supposed to ship the thing and he's like, "Got to get it out there.
Did you test it?" And you're like, "Oh, we smoke tested." It's the last few checks I suppose. And then performance testing, which is important in most applications, making sure that everything is running as it should, everything is running as quickly as it should. Nothing is slowing it down where it shouldn't. This can catch a lot of bugs in code. XC test provides some really simple mechanisms for performance testing that we use as well. It'd be fairly common as well, at least for libraries to have regression testing with performance testing, to make sure that code you introduced didn't slow things down 100X because that wouldn't be fun for anyone involved. So, let's start with unit tests. Again, unit tests focus on the smallest possible unit of code, typically a function or class, they're fast to run and you'll usually have a lot of them.
So, the example we're going to be going over today is a really simple application, a library application. There's going to be a library. There's going to be users. Users can borrow books and use just can return book. I don't know why I pick this, seemed like a nice thing that wasn't a to do app. Let's start going down and just explaining what's happening here. You have the library, you have an error enum, which is a fairly common thing to do in Swift. Sorry. You have an array of books. You have an array of member IDs. These are people with assumably library cards. I don't know, that's the thing in every country. You're going to have initialize it, that takes in an array of books and an array of member IDs that initialize the library to be in the correct state.
You're going to have a borrow method that is going to take an ISBAN, which is ... I don't exactly remember what it stands for. International something book number, it's the internationally recognized idea, the book and then the library memberUid, it is going to be a throwing method that returns a book. And just to go over to the book for a second, a book contains an ISBAN, ID, a title and an author. The borrow method is going to be the main thing that we look at here. This is an individual body of work, there's clear input and output. It takes an ISBAN and the library memberUid, and it gives you back a book if all was successful. Let's walk down what this method does and how we want to test it.
Again, receive an ISBAN, received a library memberUid. We're going to check if that book actually exists in the available books. If it doesn't, we throw an error, we're going to check if a member actually exists in our library memberUid, it doesn't, throw an error. If we've gotten to this point, our state is correct. We remove the book from the books array, and we return it back to the color. So, it can be a common mistake to only test the happy path there, I give you the right ISBAN, I give you the right Uid, I get the right book back. We also want to test the two cases where you don't have the correct book, you don't have the correct member. And that the correct error is thrown. So, go to import XC test and write our first unit test.
Throwing method, it is going to ... I'll go line by line. I'm not going to do this for every single slide. But because we're just kind of getting warmed up here, it'll make it clear what I'm talking about as we progress with the example because we're going to build on the example as the presentation goes on. So, we're going to create a new library. It's going to have an empty array of books and empty memberUids. We're going to try to borrow a book with an ISBAN that doesn't exist in the array and a random Uid which naturally does not exist in the empty number ID. That's going to throw an error. We're asserting that it throws an error. This is bad path, but it's good that we're testing it. We should also be checking that it's the correct error.
I did not do that to save space on the slide. The wonders of presenting. After that, we're going to create a library now with a book, but not a Uid, that book is going to make sure that the first check passes, but the lack of memberUids is going to make sure that the second check fails. So we're going to try to borrow that book again. That book is Neuromancer, which is great book. Everybody should read it. Add it to your summer reading lists, got plenty of time on our hands. We're going to assert that, that throws an error. After that we're going to actually create the array of memberUids finally, we're going to create another library with the Neuromancer book and the memberUids properly initialized this time. And we're going to finally successfully borrow the book using the first member of that members array of IDs.
That book, we're going to assert that has been the correct title and the correct author. We tested both bad paths in the happy path. There's probably more we could have tested here. We could have tested the library, initialized it to make sure that the state was set up soundly. That gets a bit murky though, when you have private fields, generally a big no, no in testing is to avoid unprivate things that should be private. That means that you're probably testing wrong or something was structured wrong. So for the most part, this test is sound, this is the basic unit test. Integration tests, integration tests ensure that the interlocking pieces of your application work together as designed. Sometimes this means testing layers between classes, and sometimes this means testing layer between your database and application. So considering that this is the Realm user group, let's consider Realm as your object model and the database that we will be using and testing against.
So, we're going to switch some things around to work with Realm. It's not going to be radically different than what we had before, but it's going to be different enough that it's worth going over. So our book and library classes are going to inherit an object now, which is a Realm type that you inherit from so that you can store that type in the database. Everything is going to have our wonderful Abruzzi Syntex attached to it, which is going away soon, by the way, everyone, which is great. The library class has changed slightly and so far is that has a library ID now, which is a Uid generated initialization. It has a Realm list of available books and a Realm list of library members. Library member is another Realm object that has a member ID, which is a Uid generated on initialization.
A list of borrowed books, as you can borrow books from the library and the member ID is the primary key there. We are going to change our borrow method on the library to work with Realm now. So it's still going to stick and it has been in a memberUid. This is mainly because we're slowly migrating to the world where the borrow function is going to get more complex. We're going to have a check here to make sure that the Realm is not invalidated. So every Realm object has an exposed Realm property on it that you can use. That is a Realm that is associated with that object. We're going to make sure that that's valid. We're going to check if the ISBAN exists within our available books list. If that passes, we're going to check that the member ID exists within our members list of library members. We're going to grab the book from the available books list. We're going to remove it from the available books list and we're going to return it to the color. As you can see, this actually isn't much different than the previous bit of code.
The main difference here is that we're writing to a Realm. Everything else is nearly the same, minor API differences. We're also going to add a return method to the library member class that is new. You should always return your library books. There's fines if you don't. So it's going to take a book and the library, we're going to, again, make sure that the Realm is not validated. We're going to make sure that our list of borrowed books because we're borrowing books from a library contains the correct book. If it does, we're going to remove it from our borrowed books list and we're going to append it back to the list of bell books in the library. So, what we're already doing here in these two methods is containing business logic. We're containing these things that actually change our data and in effect we'll eventually when we actually get to that part change the view.
So, let's test the borrow function now with Realm. Again, stepping through line by line, we're going to create an in-memory Realm because we don't actually want to store this stuff, we don't want state to linger between tests. We're going to open the Realm. We're going to create that Neuromancer book again. We're going to create a library member this time. We're going to create a library. We don't need to pass anything in this time as the state is going to be stored by the Realm and should be messed with from the appropriate locations, not necessarily on initialization, this is a choice.
This is not a mandate simplicity sake or a presentation. We're going to add that library to the Realm and we're going to, because there's no books in the library or members in the library assert that it's still froze that error. We don't have that book. Now, we're going to populate the library with the books in a right transaction. So, this is where Rome comes into play. We're going to try to borrow again, but because it doesn't have any members we're going to throw the air. Let's add members. Now we can successfully borrow the book with the given member and the given book, we're going to make sure that the ISBAN and title and author are sound, and that's it. It's nearly the same as the previous test.
But this is a super simple example and let's start including a view and figuring out how that plays in with your business logic and how Realm fits in all that. Testing business logic with Realm. Here's a really simple library view. There's two observed objects on it, a library and a library member. They should actually be observed Realm objects but it's not a perfect presentation. And so for each available book in the library, display a text for the title, a text for the author and a button to borrow the book. We're going to try to borrow, and do catch. If it succeeds, great. If it doesn't, we should actually show an error. I'm not going to put that in presentation code and we're going to tag the button with an identifier to be able to test against it later.
The main thing that we want to test in this view is the borrow button. It's the only thing that actually isn't read only. We should also test the read only things to make sure that the text user sound, but for again, second presentation, make sure that borrowing this book removes the book from the library and gives it to the member. So the thing that we at Realm have been talking about a lot recently is this MBI pattern, it meshes nicely with SwiftUI because of two-way data binding because of the simplicity of SwiftUI and the fact that we've been given all of the scaffolding to make things simpler, where we don't necessarily need few models, we don't necessarily need routers. And again, you might, I'm not mandating anything here, but this is the simplest way. And you can create a lot of small components and a lot of very clear methods on extensions on your model that make sure that this is fairly sound.
You have a user, the user has intent. They tap a button. That button changes something in the model. The model changes something in the view, the user sees the view fairly straightforward. It's a circular pattern, it's super useful in simpler circumstances. And as I found through my own dog fooding, in a new application, I can't speak to applications that have to migrate to SwiftUI, but in a new application, you can intentionally keep things simple regardless of the size of your code base, keep things small, keep your components small, create objects as you see fit, have loads of small functions that do exactly what they're supposed to do relative to that view, still a way to keep things simple. And in the case of our application, the user hits the borrow button. It's the tech button that we have.
It's going to borrow from the library from that function, that function is going to change our data. That data is going to be then reflected in the view via the Realm. The Realm is going to automatically update the view and the user's going to see that view. Fairly straightforward, fairly simple, again, works for many simple use cases. And yeah, so we're also going to add here a method for returning books. So it's the same exact thing. It's just for the member. I could have extracted this out, but wanted to show everybody it's the same thing. Member.borrowed books, texts for the title, text for the author, a return button with an accessibility identifier called return button that actually should have been used in the previous slide instead of tag. And that member is going to return that book to the library.
We also want to test that and for us in the case of the test that I'm about to show, it's kind of the final stage in the test where not only are we testing that we can borrow the book properly, but testing that we can back properly by pressing the borrow and return. So we're going to create a simple UI test here. The unit tests here that should be done are for the borrow and return methods. So, the borrow tests, we've already done. The return test, I'm going to avoid showing because it's the exact same as the borrow test, just in the case of the user. But having UI test is also really nice here because the UI in the case of MDI is the one that actually triggers the intent, they trigger what happens to the view model ... the view. Sorry, the model.
In the case of UI tests, it's actually kind of funky how you have to use it with Realm, you can't use your classes from the executable, your application. So, in the case of Realm, you'll actually have to not necessarily copy and paste, but you'll have to share a source file with your models. Realm is going to read those models and this is a totally different process. You have to think of it as the way that we're going to have to use Realm here is going to be a bit funky. That said, it's covered by about five lines of code.
We're going to use a Realm and the temporary directory, we're going to store that Realm path in the launch environment. That's going to be an environment variable that you can read from your application. I wouldn't consider that test code in your app. I would just consider it an injection required for a better structured application. The actual last line there is M stakes, everyone. But we're going to read that Realm from the application and then use it as we normally would. We're going to then write to that Realm from the rest of this test.
And on the right there is a little gift of the test running. It clicks the borrow button, it then clicks the return button and moves very quickly and they don't move as slow as they used to move. But let's go over the test. So, we create a new library. We create a new library member. We create a new book. At the library, we add the member and we append the book to the library. We then launch the application. Now we're going to launch this application with all of that state already stored. So we know exactly what that should look like. We know that the library has a book, but the user doesn't have a book. So, UI testing with SwiftUI is pretty much the same as UI kit. The downside is that it doesn't always do what you expect it to do.
If you have a heavily nested view, sometimes the identifier isn't properly exposed and you end up having to do some weird things just for the sake of UI testing your application. I think those are actually bugs though. I don't think that that's how it's supposed to work, I guess keep your eyes peeled after WWDC. But yeah, so we're going to tap the borrow.button. That's the tag that you saw before? That's going to trigger the fact that that available book is going to move to the member, so that list is going to be empty. We're going to assert then that the library.members.first.borrowbooks.firststudy is the same as the book that has been.
So, the first and only member of the library's first and only book is the same as the book that we've injected into this application. We're then going to hit the return button, that's going to return the book to the library and run through that return function that you saw as an extension on the library member class. We're going to check that the library.members.borrowbooks is empty. So, the first and only member of the library no longer has a borrowed book and that the library.borrowbook at first, it has been the only available book in the library is the same as the book that we inject into the application state. Right. So, we did it, we tested everything, the application's great. We're invincible. We beat the game, we got the high score and that's it.
But what about more complex acts, you say? You can't convert your 50,000 line app that is under concentrator to these simple MVI design pattern now? It's really easy to present information in this really sterile, simple environment. It's kind of the nature of the beast when it comes to giving a presentation in the first place. And unfortunately, sometimes it can also infect the mind when coming up with features and coming up with ways to use Realm. We don't get to work with these crazy complex applications every day, especially ones that are 10 years old.
Occasionally, we actually do get sent people's apps and it's super interesting for us. And we've got enough feedback at this point that we are trying to work towards having Realm be more integrated with more complex architectures. We don't want people to have to work around Realm, which is something we've seen, there are people that completely detach their app from Realm and use Realm as this dummy data store. That's totally fine, but there's often not a point at this point in doing something like that. There's so many better ways to use Realm that we want to introduce features that make it really obvious that you don't have to do some of these crazy things that people do. And yes, we have not completely lost our minds. We know there are more complex apps out there. So let's talk about MVVM.
It is just totally off the top of my head, not based on any factual truth and only anecdotal evidence, but it seems to be the most popular architecture these days. It is model view view model. So, the view gives commands to the view model, the view model updates the model, the view model reads from the model and it binds it to the view. I have contested in the past that it doesn't make as much sense with SwiftUI because it's two way data binding because what ends up happening with the models in SwiftUI is that you write from the view to the view model and then the view model just passes that information off to the model without doing anything to it. There's not really a transformation that generally happens between the view model and the model anymore. And then you have to then manually update the view, and especially with Realm where we're trying to do all that stuff for you, where you update the Realm and that updates the view without you having to do anything outside of placing a property wrapper on your view, it kind of breaks what we're trying to do.
But that said, we do understand that there is a nice separation here. And not only that, sometimes what is in your model isn't necessarily what you want to display on the view. Probably, more times than not, your model is not perfectly aligned with your view. What happens to you if you have multiple models wrong, doesn't support joins. More often than not, you have used with like a bunch of different pieces. Even in the example I showed, you have a library and you have a library member, somebody doing MVVM would want only a view model property and any like super simple state variables on that view. They wouldn't want to have their objects directly supplanted onto the view like that. They'd have a library view view model with a library member and a library. Or even simpler than that. They can take it beyond that and do just the available books and the borrowed books, since those are actually the only things that we're working with in that view.
So this is one thing that we've seen people do, and this is probably the simplest way to do view models with Realm. In this case, because this view specifically only handles available books and borrowed books, those are the things that we're going to read from the library and the library member. We're going to initialize the library view view model with those two things. So you're probably do that in the view before, and then pass that into the next view. You're going to assign the properties of that from the library available books and the member borrowed books, you're then going to observe the available books and observe the borrowed books because of the way that ... now that you're abstracting out some of the functionality that we added in, as far as observation, you're going to have to manually update the view from the view model.
So in that case, you're going to observe, you don't care, what's changing. You just care that there's change. You're going to send that to the object will change, which is a synthesized property on an observable object. That's going to tell the view, please update. Your borrow function is going to look slightly differently now. In this case, you're going to check for any available books, if the ISBAN exists you can still have the same errors. You're going to get the Realm off of the available books which, again, if the Realm has been invalidated or something happened, you are going to have to throw an error. You're going to grab the book out of the available books and you're going to remove it from the available books and then append it to the borrowed books in the right transaction from the Realm, and then return the book.
So, it's really not that different in this case. The return function, similarly, it does the opposite, but the same checks and now it even has the advantage of both of these are on the singular model associated with a view. And assuming that this is the only view that does this thing, that's actually not a bad setup. I would totally understand this as a design pattern for simplifying your view and not separating things too much and keeping like concepts together. But then we've seen users do some pretty crazy things, like totally map everything out of Realm and just make their view model totally Realm agnostic. I get why in certain circumstances this happens. I couldn't name a good reason why to do this outside of like there are people that totally abstract out the database layer in case they don't want to be tied to Realm.
That's understandable. We don't want people to be handcuffed to us. We want people to want to use us and assume that we will be around to continue to deliver great features and work with everyone to make building apps with Realm great. But we have seen this where ... Sure, you have some of the similar setup here where you're going to have a library and a library member, but you're going to save out the library ID and the member ID for lookup later. You're going to observe the Realm object still, but you're going to map out the books from the lists and put them into plain old Swift arrays.
And then basically what you're going to end up doing is it's going to get a lot more complex or you're going to have to look up the primary keys in the Realm. You're going to have to make sure that those objects are still sound, you're then going to have to modify the Realm objects anyway, in a right transaction. And then you're going to have to re-map out the Realm lists back into their arrays and it gets really messy and it ends up becoming quintessential spaghetti code and also hard to test, which is the point of this presentation. So, this is not something we'd recommend unless there's good reason for it. So there's a big cancel sign for you.
We understand that there are infinite use cases and 1,000 design patterns and so many different ways that you can write code, these design patterns or social constructs, man. There's no quick and easy way to do this stuff. So we're trying to come up with ways to better fit in. And for us that's projections, this is a pre-alpha feature. It's only just been scoped out. We still have to design it fully. But this is from the prototype that we have now. So what is a projection? So in database land projection is when you grab a bunch of data from different sources and put it into a single structure, but it's not actually stored in the database. So, if I have a person and that person has a name and I have a dog and that dog has a name and I want to project those two names into a single structure I would have like a structure called person and dog name.
I would do queries on the database to grab those two things. And in Mongo, there's a project operator that you can use to make sure that that object comes out with the appropriate fields and values. For us, it's going to look slightly different. At some point in the future, we would like a similar super loose projection syntax, where you can join across multiple objects and get whatever object you want back. That's kind of far future for us. So in the near future, we want to come up with something a bit simpler where you're essentially reattaching existing properties onto this new arbitrary structure. And arbitrary is kind of the key word here. It's not going to be directly associated with a single Realm object. It's going to be this thing that you associate with whatever you want to associate it with.
So if you want to associate it with the view, we've incidentally been working on sort of a view model for people then it becomes your view model. If the models are one-to-one with the view, you grab the data from the sources that you want to grab it from. And you stick it on that projection and associate that with the view. Suddenly, you have a view model. In this case, we have our library view view model, it inherits from the projection class or protocol, we're not sure yet. It's going to have two protective properties. It's going to have available books and borrowed books. These are going to be read directly from the library and member classes. These properties are going to be live. Think of this as a Realm object. This is effectively a Realm object with reattached successors.
It should be treated no differently, but it's much more flexible and lightweight. You can attach to anything on here, and you could attach the member IDs on here, if you had overdue fees and that was supposed to go on this view, you could attach overdue fees to it. There's things you can query. Right now we're trying to stick mainly to things that we can access with keypads. So, for those familiar with keypads, which I think was Swift 52.
I can't remember which version of Swift it was, but it was a really neat feature that you can basically access a chain of keypads on an object and then read those properties out. The initial version of projections will be able to do that where that available books is going to be read from that library and any updates to the library, we'll update available books, same thing with borrowed books and library member. And it's going to have a similar borrow function that the other view model had, this case it's just slightly more integrated with Realm, but I think the code is nearly identical. Same thing with return code is nearly identical, slightly more integrated with Realm.
And the view is nearly the same, except now you have the view model, sorry for some of the formatting there. In this case, you call borrow on the view model and you call return on the view model. It is very close to what we had. It's still a Realmy thing that's going to automatically update your view when any of the things that you have attached update so that if the library updates, if the user ... Oops, sorry, not user. If the member updates, if the books update, if the borrowed books update, that view is again going to automatically update. And now we've also created a single structure, which is easier to test or for you to test. Integration testing is going to be, again, very, very similar. The differences is that instead of creating a library and a member, creating we're also creating a library view model.
We're going to borrow from that, we're going to make sure that it throws the appropriate error. We're going to refill the state, mess with the state, do all the same stuff, except this time on view model. And now what we've done here is that if this is the only place where you need to return and borrow, we've created this nice standalone structure that does that for you. And it's associated with Realm, which means that it's closer to your model, since we are encouraging people to have Realm B of the model as a concept. Your testing is the exact same because this is a view associated thing and not actually a Realm object, you don't need to change these tests at all. They're the same. That's pretty much it. I hope I've left enough time for questions. I have not had a chance to look at the chat yet.
Ian: I'm going to see to that, Jason, but thank you so much. I guess one of the comments here, Sebastian has never seen the objective C declaration that we have in our Realm models. Maybe tell them a little bit about the history there and then tell him what's in plan for future.
Jason: Sure. So, just looking at the question now, I've never used an ob C member. Obviously, members prevents you from having to put at ob C on all of your properties that need to use objective C reflection. The reason that you have to do that with Realm and Swift is because we need to take advantage of objective C reflection. It's the only way that we're able to do that. When you put that tag there, sorry, annotation. When you put that there, it gives objective C, the objective C runtime access to that property. And we still need that. However, in the future, we are going to be taking advantage of property wrappers to make it a much nicer, cleaner, more obvious with syntax. Also, it's going to have compile time checks. That's going to look like Swift instead of an ob C whatever. That is actually coming sooner than later. I hesitate to ever promise a date, but that one should be pretty, pretty soon.
Ian: Excellent. Yeah, we're looking forward to being able to clean up those Realm model definitions to make it more swifty. Richard had a question here regarding if there's a recommendation or proper way to automate the user side input for some of the UI testing?
Jason: UI testing, for UI test proper, is there a way to automate the user input side of the equation since you weren't going to? I'm not entirely sure what you mean, Richard. If you could explain a bit more.
Ian: I mean, I think maybe this is about having variable input into what the user puts into the field. Could this also be maybe something around a fuzzer, having different inputs and testing different fields and how they accept certain inputs and how it goes through different tests?
Jason: Yeah. I mean, I didn't go over fuzz testing, but that's absolutely something that you should do. There's no automated mouse input on text input. You can automate some of that stuff. There's no mouse touch yet. You can touch any location on the screen, you can make it so that if you want to really, not load test is the wrong word, but batch up your application, just have it touch everywhere and see what happens and make sure nothing crashes, you could do that. It's actually really interesting if you have these UI tests. So yes, you can do that, Richard. I don't know if there's a set of best standards and practices, but at least with macOS, for instance, it was bizarre the first time I ran it. When you a UI test on macOS it actually completely takes control from your mouse, and it will go all over the screen wherever you tell it to and click anywhere. Obviously, on the iPhone simulator, it has a limited space of where it can touch, but yes, that can be automated. But I guess it depends on what you're trying to test.
Ian: I guess, another question for me is what's your opinion on test coverage? I think a lot of people would look to have everything be unit tested, but then there's also integration tests. Should everything be having integration tests? And then end to end tests, there's kind of a big, a wide berth of different things you can test there. So, what's your opinion on how much coverage you should have for each type of test?
Jason: That's a tough question, because at Realm, I suppose we tell ourselves that there can never be too many tests, so we're up to us, every single thing would be tested within reason. You can't really go overkill unless you start doing weird things to your code to accommodate weird testing patterns. I couldn't give you a number as to what appropriate test coverage is. Most things I know for us at Realm, we don't make it so that every single method needs to be tested. So, if you have a bunch of private methods, those don't need to be tested, but for us, anything by the public API needs to be heavily tested, every single method and that's not an exaggeration. We're also a library. So in a UI application, you have to look at it a bit differently and consider what your public API is, which were UI applications, really the entry points to the model, any entry point that transforms data. And in my opinion, all of those should be tested. So, I don't know if that properly answers the question, for integration tests and end to end tests, same thing. What?
Ian: Yeah, I think so. I mean, I think it says where's your public API and then a mobile application, your public API is a lot of the UI interfaces that they can interact with and that's how they get into your code paths. Right?
Ian: I guess another question from me, and this is another opinion question is what's your opinion on flaky tests? And so these are tests that sometimes pass sometimes fail and is it okay? A lot of times relate to, should we release, should we not release? Maybe you could give us a little bit of your thoughts on that.
Jason: Yeah. That's a tricky one because even on the Realm Cocoa, if you follow the pull requests, we still have a couple of flaky tests. To be honest, those tests are probably revealing some race condition. They could be in the test themselves though, which I think in the case of some of the recent ones, that was the case. More often flaky tests are revealing something wrong. I don't want to be on a recording thing that that's okay. But very occasionally, yes, you do have to look at a test and be like, "Maybe this is specific to the testing circumstance," but if you're trying to come out with like the most high quality product, you should have all your tests passing, you should make sure that there's no race conditions, you should make sure that everything is clean cut sound, all that kind of thing.
Ian: Yeah. Okay, perfect. There's a final question here, will be docs on best practices for testing? I think we're looking as this presentation is a little bit of our, I wouldn't say docs, but a presentation on best practices for testing. It is something potentially in the future we can look to ask to our docs. So yeah, I think if we have other things covered, we can look to add testing best practices as well to our docs as well. And then last question here from Shane what are we hoping for for WWDC next week?
Jason: Sure. Well, just to add one thing to Ian's question, if there's ever a question that you or anybody else here has, feel free to ask on GitHub or forums or something like that. For things that we can't offer through API or features or things that might take a long time to work on, we're happy to offer guidance. We do have an idea of what those best practices are and are happy to share them. As far as WWDC, what we're hoping for is ..., yes, we should add more docs, Richard, sorry. There are definitely some things there that are got yous. But with WWDC next week, and this ties to best practices on multi-threading using Realm, we're hoping for a sync await which we've been playing with for a few weeks now. We're hoping for actors, we're hoping for a few minor features as well like property wrappers in function parameters and property rappers in pretty much Lambdas and everywhere. We're hoping for Sendable as well, Sendable will prevent you from passing unsafe things into thread safe areas, basically. But yeah, that's probably our main wishlist right now.
Ian: Wow. Okay. That's a substantial wishlist. Well, I hope you get everything you wish for. Perfect. Well, if there's no other questions, thank you so much, everyone, and thank you so much, Jason. This has been very informative yet again.
Jason: Thanks everyone for coming. I always have-
Ian: Thank you.
Jason: ... to thank everyone.