Beyond the Framework: Distributing Your Desktop App Like a Pro

Rate this content

Building apps using web technology is great; however, at times you may encounter limitations regardless of what framework you choose. During this workshop, we will talk about choosing a framework, common problems and how to overcome them.

Table of contents

- Introduction: The intertwined history of the Web and Apps

- So many desktop app technologies! How do I choose one?

- Common issues and how to think about apps

- Conclusion

Jonas Kruckenberg
Jonas Kruckenberg
109 min
16 May, 2023


Sign in or register to post your comment.

Video Summary and Transcription

Welcome to the Beyond the Framework workshop where we will discuss desktop apps and their development. We will focus on what makes a desktop app unique, how to choose a framework, and how to structure your code. The web and apps are intertwined, with a history dating back to the 1940s. When choosing a technology, consider core features and compatibility with existing frameworks like React and Svelte. Building an app is more complicated than a hello world app, and common issues in app development include understanding the main and render threads, synchronizing app state across windows, and separation of concerns. Tauri is a Rust crate that allows you to build your own desktop app, and it aims to make building Tauri apps accessible without the need to learn Rust. The main thread and render thread model is important in app development, and it allows for multi-threading in web browsers. The state is owned by the main thread and synchronized between all render threads. The concept of a native feeling UI is subjective and varies across operating systems. Packaging and distribution are essential for desktop apps, involving building, packaging, code signing, and distribution. The goal is to make building desktop apps as easy as deploying a website, and the Tauri working group is focused on improving the build process, user experience, and distribution of desktop apps.

1. Introduction to Beyond the Framework Workshop

Short description:

Welcome to the Beyond the Framework workshop where we will discuss desktop apps and their development. We will focus on what makes a desktop app unique, how to choose a framework, and how to structure your code. I am a member of the Towery Working Group and a DevRel at Kript Nebula. Towery is a framework for building desktop apps using web technology, and Kript Nebula is a company that helps take desktop apps to the next level. I also maintain open source libraries and tools in the Vite ecosystem and Rust crates.

Welcome. My workshop called Beyond the Framework, where we will be talking about desktop apps, but not focused on one specific framework. We will be taking things beyond the framework. We will be focusing on what makes a desktop app a desktop app, how to build one and what to focus on and not get hung up on the details. Because at the end of the day, a framework is just a tool.

So it's going to be the agenda for this workshop. Well, at first, I'm going to tell you a bit about the history of apps and the history of the web, because I feel like that's something that's very much overlooked. Then we're going to talk a bit about how you choose a framework, what things to look out for, and a few categories that we found helpful when choosing a framework. Then lastly, we're going to talk about how to think about apps, how to structure your code, how to work with desktop apps, and a few things that are different to your traditional website development, maybe, and stuff to look out for.

Who am I? Who's the guy talking to you? I am a member of the Towery Working Group, but I'm also a DevRel at Kript Nebula. And to explain that a bit more, Towery, if you're not familiar, it's a framework for building desktop apps using web technology. And we focus very much on small binaries, on secure apps, and on what I like to call choice, because Towery is a complete framework. That will make a bit more sense later when we jump into the exercises. And yeah, but I'm also a devrel at Kript Nebula. Kript Nebula is a company, as a company, we founded to really take things further and to help you, potentially, companies to take their desktop apps to the next level. And we do consulting, we do auditing, and we're also working on tools to improve the desktop app experience using web technology. I also maintain a couple open source libraries and tools and crates in the Vite ecosystem, for example, and then Rust crates. Because I started with JavaScript, I did that for a long time, but now, since about a year, I picked up Rust, and I really like it. And so if you look on Twitter, that has come up time and time again, people from the JavaScript being interested in Rust. If you are one of them and you want to talk to me about the problems you're facing, or you are unsure how to start, definitely reach out to me on Twitter or Mastodon. The handles are on screen right now. Cool. That's about me.


Preparing for Exercises and Q&A

Short description:

Let's prepare for the exercises and check out the apps we'll be discussing. Instructions and links are available on the website and in the chat. You can choose to use Tauri, Electron, or PWA depending on your language preference. If you have any questions, feel free to ask at any time.

Next, let's prepare for the exercises. We have a couple of exercises coming up to let you play around with a few apps, and to check out the things that I'm going to be talking about for yourself. Instructions are on this website, so I'm going to put the same link in chat. You don't need to scan the QR code if you don't want to or type up the URL yourself. There you will see a link to... There will be a card that says workshop, and then there will be a link to the assets, which they are a Git repository on GitHub. You can check that out. You should make sure, if you want to follow along using the Tauri Framework, that you also have the prerequisites but the link to that should also be in the repo. Everything is self-contained in that one repo. We have a choice. Depending on what language you're looking for, if you're sort of wanting to try a bit of rust, then we have Tauri. If you just want to stay in JavaScript you can also follow along using Electron. Lastly, we have PWA as a choice as well. Progressive Web App. I feel like that should be good for everyone. Those are branches in that repo. You can check out the branch you want. The branches are named accordingly. If you are just here to vibe and just want to read along you can of course also do that. Cool. Oh, that's one thing more to mention. We're all here to have a good time. If you have any questions, don't hesitate. Keep them to the end. Don't forget about them. Feel free to ask them and chat at any time and I will make sure to answer them if I can.

The Interconnection of Web and Apps

Short description:

The web and apps are intertwined, with a history dating back to the 1940s. From Vannevar Bush's Memex to Douglas Engelbart's online system and Xerox PARC's GUI operating system, the concept of hypertext and interlinked documents influenced the development of the web. The progression from apps to websites began with Tim Berners-Lee's invention of the World Wide Web in 1989. The line between native apps and web apps has become increasingly blurry, with projects like Apple's use of web apps on the iPhone and Google's Project Fugu blurring the line even further. When choosing a technology, consider core features and compatibility with existing frameworks like React and Svelte.

Cool. History lesson. Great. Learning something. The web and apps because this is something that I feel is often overlooked and something that people don't really know about. That's very interesting to me because often you hear people saying, oh, you build an app using web technology, but that's not native. That's not a real app. That's not proper. And I dug a bit deeper and the web and apps are very much intertwined.

So starting out in 1945, the academic work that led up to computers that are used by what was then called knowledge workers, what we would now call just regular people using computers, Vannevar Bush, I don't really know if I'm pronouncing that correctly, but he came up with a system called Memex, which was meant to augment the sort of human mind using computers. And this was very much sort of theoretical. And then later on, just because in the 40s, the computer technology really, really wasn't advanced enough to sort of put his ideas into practice.

But in the 1960s, Douglas Engelbart, you may have heard the name, also one of the pioneers of computing technology. He worked on the online system, which was very much inspired by what Vannevar Bush proposed, and something that would then and that's sort of the interesting part, which would then later go on to influence a lot of the hypertext. They were also talking about, you know, links and interlinking documents, and things like that. And out of this sort of research in the 1960s, a lot of the people that worked on that project with Douglas Engelbart, then went on to go work at Xerox PARC, where they were working on one of the first personal computers with a GUI operating system.

And again, a lot of the inspiration in the back of their minds came from this knowledge worker interlinked documents, argumenting the human mind. And this, you know, to give you sort of a bit of perspective, this was very early in time, you know, you can see here, 1970s to 1980s. A lot of this was then still stuck, you know, in research and in academic circles, but then, you know, 1985, rolled around Windows 1.0 was released. So you know, widespread if you will in the world of computers adoption of graphical operating systems.

And then in 1987, this is sort of a often talked about very important concept or very important product, not, you know, not used much, but as an influence, the Apple Hypercard was one of the earliest, that I know about that I could find earliest systems where users could sort of program their own computer in a graphical way. And sort of have this very easy to use programmable interface to their computers, using cards and stacks of cards, very sort of a very physical metaphor, but again, very much influenced the work, you know, the theoretical work on hypertext and on, you know, augmenting the human mind. But not in any way related to anything that we would consider the web.

So this was like purely like building sort of small apps on your computer. So, you know, the original low-code, no-code app, if you will. And then in 1989, this is where we started to pick up web steam now, finally. Tim Berners-Lee invents the World Wide Web. And, again, not the same group of people, but again influenced by, you know, hypertext and interlinked documents and this whole notion of taking a computer to augment the human mind. And now we're sort of progressing into web page territory, right? Up until this point, everything was apps and what you would consider native. And now we suddenly have websites. Of course, you know, very basic at first. And then 1993, Mosaic released. And with that sort of progression started what, well, actually, you know, all of the modern browsers we have to this, to this day.

This just, you know, very brief excerpt because you know, I don't want to bore you. Just goes to show that right from the beginning of the web and apps, this has been very much interlinked. So the notion of hypertext and hyperlinks and interlinked documents has been with computers, not just with the web, but with computers from the very beginning. Very much all of the UI and GUI operating systems and GUI concepts that have come out of the computer science in that very early time, they're very much inspired by hypertext, which is crazy if you think about it, because the World Wide Web as a concept, the web that we work in to this day, came much later than that.

I feel like this arc has a very nice arc to it, because if we sort of jump ahead to the modern day now, the announcement, recent history by Apple that the iPhone would not be running native apps, it would be running web apps. Crazy if you think about it, but just goes to show that the line, and if you are on Twitter or Reddit or Hacker News, the line that people like to draw on the sand, that says, this app is native and this app is not native, that line is very blurry. If it exists at all.

And this line just gets even blurry as time goes on because you may have not heard about the name, but Google in 2019 announced what they called Project Fugu, which is just sort of a tracking effort for a lot of the web APIs that we have recently got. Things like web media or interfacing with USB devices, things like that. All of these APIs that really blur the line again between what a website can do and what an app can do, the project really started to sort of blur the line again. If we had in the early 2000s, if we had sort of a very clear separation between what websites are and what native apps are, then now, again, just as in the early days, this line is very much blurring right now.

So there are a lot of technologies to do this, right? And one question that we always get is so many options, right? What do I do? How do I choose one? And I have a few. I have a few select choices. There are many, many more choices out there. And what I always tell people and what we always tell people is, there are a couple of categories that you should look out for. And most of these should, if you are a web developer, sound familiar, right? Because at the end of the day, a lot of the categories you choose the web front-end framework by are very similar to what you would choose a desktop app framework by. For example, the first category I always tell people is core features, right? Like what are the features of the framework? What sort of what is supported? And then that, for example, includes like, Hey, I'm very experienced in this front-end framework or my team uses React, my team uses Svelte.

Framework Considerations and Code Overview

Short description:

When choosing a framework, consider its support for your front-end framework and web APIs. The developer experience should be as easy as developing websites, including local development and library support. Packaging and distribution are crucial for desktop apps. Learning resources and operating system integrations are also important factors to consider.

Does this framework that I'm about to pick up, does it support my front-end framework or what are the web APIs support right? My app, for example, uses notifications, is that supported? Can I use it? Things like that, right? That's sort of a very, very basic category.

The next one, and I put this at this, you know, I put this at the sort of front of this list, because I feel like it's way more important than people give it credit for it's sort of the developer experience, right? Because we all know web development today is very easy. And we have all grown used to web development being very easy, right? I can spin up a V server, it starts up very fast. Oh, excuse me. Sorry. You know, you can set up a local development server, starts up very fast, very nice to use. We have a great ecosystem of, you know, libraries on npm and so forth. So, ideally, the desktop framework that you choose should reflect that, right? The desktop framework, the whole experience should be as easy as working with a regular website. Of course that isn't necessarily sort of always the case, because you're adding a layer of complexity on top of it with a desktop app framework. But ideally, you know, the developer experience should be as easy as developing websites. And that includes, you know, as I mentioned, local development, how fast is it to start up? Is it, you know, feeling nice in my machine? Is it integrating with all the tools that are already used, but then also the recording should be enabled? I hope. Okay. Cool. Sorry. Interrupted. But then, as I mentioned with NPM, does it support my libraries? You know, no website, no app is developed completely from scratch. So, is my library supported? Can I pull it in very easily? Is that a good experience? And then lastly, the one thing that desktop apps and websites, you know, where's the very difference is packaging and distribution, right? Because with, because with websites, what you do is you find yourself a web server, a web host. Oftentimes nowadays, right, you can just connect your Github repo to the service and it'll deploy for you. But with desktop apps, you need to go through that step of packaging up your app, bundling it, putting it up into the app store. Code signing it, and that's, you know, we will get to that in more detail but that's all part of the developer experience, and to me that is very important.

Similarly, very important learning resources, right? Chances are that you were not born an expert in any of these frameworks, right? So what's the way to learn it? You know, is there good documentation, are there many videos out there on YouTube or any other platforms to learn this if you're sort of a more visual person. Are there blog posts? You know, is there a way to be able to learn it? And then the question is, is there a forum you can ask questions? Or even formal training in any way, shape, or form? And then one thing that I also feel as a non-native English speaker that gets often overlooked is, are all of these resources also available in my language? You know, is it something that I can very easily pick up? Especially if I'm and don't want to deal with the overhead of then parsing it in English. Learning resources are very important as a category.

And then lastly, this is very specific to desktop apps, you're likely building desktop apps because you want to go beyond what the web can offer, right? You want to have tighter integration with the operating system. You want to have access to the resources, the full resources that the computer offers you without going sort of through the indirection of the sandbox that the browser builds. And then it becomes very important to look at, okay, what operating system integrations do I need? Are they supported by my framework? Things like creating windows, very basic. Most of them can do that. And then, but also more complicated things like notifications or a file system. And then also of course, which operating systems are imported. Sorry, are supported. What's the support on Linux, for example? Because, as much as I love Linux, every distribution almost counts as its own mini operating system. So what is the operating system support, you know? Is that operating system that I need, is the one that I care about, is that supported, or the one that my users care about, right? So those are the four categories that I have, you know, or we have, identified as being very, very important when you consider which framework to choose, what to look at, and there are, of course, you know, more and then it gets more nuanced, but sort of as a quick introduction, those are the most important ones to look out for.

And with that, the boring bit out of the way for now, let's jump into some code. Shall we? So, the way this works and let me exit, yeah, so, I hope you can see this. What I have up running right now are three apps that all say, Hello JS Nation! And those are the three apps, you know, the three ways that you, well, not the only three ways, of course, but you know, the three kinds of examples that I included, the three technologies. And I will, you know, give you a quick rundown of each one of these, Hello World apps, to kind of show you and also talk about, you know, what's different, what's the same, to give you an idea because most of them, very different. Spoiler alert, this, these are very, very, very similar. Did I say different? Then I misspoke, kind of. In my head, I thought I said different, but that's not what I mean. They are very similar. Um, cool. So, let me open up my VSCode. Oh no, before I do that. I will bring up the website. So, if you click the link that's in chat, this will bring you to this website and you can also win an Xwing. This is also for the proper JS Nation conference. But if you, you know, want to get in early, feel free. But what we're looking for is this sort of box and click on workshop assets. This brings you to this repository right here. And again, as I said, three branches, tarry, electron, PWA.

Exploring the PWA Hello World

Short description:

We will start with the PWA Hello World, which is the simplest progressive web app. Progressive web apps are websites that the browser lets you install. They consist of an HTML file and a manifest.json file that describes the app. The main index.html file contains the HTML code, and a service worker is required to make the app available offline. The service worker script is responsible for caching assets and enabling background sync. The script tag is used to load the service worker, which is registered to ensure its functionality. The service worker itself is simple and caches web assets on the first load. Desktop apps are expected to work offline, providing a seamless experience for users regardless of their internet connection.

And we will go through them one by one, starting with PWA. So if you want to follow along, clone the repository and install the dependencies. You will need Node.js for this. I hope you have it installed. If not, we can also send a link in chat or do Google for Node.js.

Um, let me bring up VS Code. Nice, cool. Here we are. So, the way I have this repository structured is that we have this folder with checkpoints in it. And each checkpoint sort of represents a point in time for our app. Right? So, as we go through this workshop, you know, I will call out, um, okay. I will tell you we are now at checkpoint one, checkpoint two, checkpoint three. And you can sort of jump ahead if you want. And later, if you sort of want to go back, you can sort of time travel. And you can use these checkpoints as sort of jumping off points for your own exploration and your own. That will sort of be important later.

So, cool. Let's start with the PWA Hello World. Shall we? And because it's really it's the simplest. And that is because progressive web apps are just websites, right? Websites that the browser lets you install. And as any website, it's an HTML file. Not quite. There's also a manifest.json file. And this manifest.json file describes your app. You can give it a name, give it a short name, and said a few other things that then will be reflected in this app. For example, I said a theme color here, and it sort of took the theme color as sort of the title bar color. But the one we really care about is this main index.html file. And you can see, very simple. We have sort of a style sheet included at the beginning to make things look good. You could, of course, also have this as a separate file. I chose to include it so we have a neat standalone html file. And then we just have our html. Hello, JS Nation, right.

The one thing that you sort of will need for a progressive web app to work is what's called a service worker. If you're not familiar with that, a service worker is sort of a separate JavaScript script that will run in the background of your browser. And that is responsible for essentially making sure your app is available offline, right? It can do a few other things as well, but this service worker, that's what sort of powers your app behind the scenes. And it can do a whole bunch more than just sort of cache your assets and make them available offline. It can also do background sync things. Like for example, you can set it to once every two hours sort of fetch new data from a server or something like that. And this script tag you see right here, this is all the code that is required to load the service worker. So we will read it from a file, and then we register it. And that takes care of that. The service worker itself is also very simple. This will not sort of change at all. We will not touch this again. And this one will just cache all of our web assets on the first load if it's not already cached. And one of the things that is different that makes it different to just a traditional website is that desktop apps are usually working offline. So what a user expects from a desktop app is I can just open it. If I'm me as a German, Germany famously not very good with mobile cell service and mobile internet. So if I'm out somewhere in rural Germany, I expect my app to start just as fast and to work just as well as when I'm home.

Running the App and Distributing Electron Apps

Short description:

A service worker is essential for running the app. To run it, an HTTP server is included as a development dependency. This checkpoint can be run using the PNPM command. PWAs can be installed as apps and work offline. In Electron, the main thread is in control of the app and can create a window with HTML. The create window function creates a new browser window and loads the index.html file. Electron apps can be packaged and distributed as standalone binaries.

And so a service worker is very essential for that. But that is about it. So what you can do to run this yourself is I included as a development dependency, I included an HTTP server and you can run that using the command that's here in the readme. This now being checkpoint one, we run it using this PNPM command. Myself, I prefer PNPM as a package manager. This repo works with NPM as well as with Yarn. Whatever your preference in the package manager department is should work as well.

We just click on Start and then we open this link and this opens just as any old website with the important difference that... Okay, wait. Let me uninstall this again. Uninstall. Yep. Oh, up again. So a PWA is just a website, right? Like you know, any old website with the important difference that the service worker and the web, sort of this manifest.json file that we have set up, make it so that here in Chrome, for example, it will prompt me or it will, you know, allow me to install this website as an app. And this is really cool because users that will sort of discover your thing through the web, they will see this and will notice, Oh, you know, there is also available as an app version. And can then just click install, and boom, it's now an app that works offline. Of course, the offline part, I cannot demonstrate to you right now because then you would not be seeing me. But this would then, you know, is now sort of available offline and works as a standalone app.

Let's jump to the electron example because the electron example is also very similar. So, if you are following along using electron, you know, navigate to the checkpoints, and then the first folder and you will see, again, we have an index.html file, right? And this one is even simpler. This is just the style sheet and, you know, the html that says, hello, JS Nation. And Electron has a very similar sort of thing to a service worker, which is in Electron-land called the main thread. And as opposed to sort of the service worker in the web world, where service worker that is optional, right? You can opt into using the service worker. Sort of the way this works is your website will be loaded first. Your website will start the service worker. And then things work offline. In Electron-land, this is exactly opposite, right? So you have the main thread that gets started first, and the main thread really like, it's in control of your app, right? The main thread, that is your app, and the main thread can then create a window with your HTML in it. So it's sort of the other way around. And this main thread is running this main.JavaScript file, main.js file. And as you can see, sort of what this file does is we wait until the app is ready, right? Let me bump up the font size for you some. Is this good? I hope. I hope you can read it. If not, let me know. So what we do is we wait until the app is ready. And then we create a window using this function right here. And this is some macOS specific code that basically replicates this behavior on macOS where, once you close a window, the app doesn't immediately exit as in Windows, but it stays around. And then once you click the icon again, it will sort of bring up a window again. This is, these lines right here are very macOS specific. Just so you know, the meaty part of this file is the create window function. And this will create a new browser window, with a pre-determined width and height. And then we instruct the main window to load our index.html file. So as I said, this works a bit differently, sort of the exact other way, where our main sort of thread is loading the html and not the html loading the service worker, quote unquote main thread. And yeah, if you can, again, run this using the Start command, so let me bring up this terminal some more. If you run Start in this folder or using this filter syntax, oh, opened on the wrong monitor, but here we are. It opened a native app, again, saying, hello, JSON Engine. And this app, as opposed to, and this is sort of a difference, this is a difference to what we've seen previously. This app, you could then sort of package up and distribute as any app. The problem with PWAs, or it's not a problem, it's a difference. The difference with PWAs is that you have to always discover them through your browser, whereas with Electron, you can distribute them as standalone binaries that work regardless of any browser or anything like that. Let's close this.

Tauri App Development

Short description:

If you are following along using Tauri, you will notice that the structure is somewhat different. Tauri is a Rust crate that allows you to build your own desktop app, giving you control over the Rust binary. Tauri binaries are smaller and more secure, as JavaScript files are baked into the app at compile time. The main.js file is the equivalent of Electron's main.js file, creating the main thread and running the app. The tauri.conf.json file is similar to manifest.json in PWAs, but with more options for desktop apps. The Tauri working group aims to make building Tauri apps accessible without the need to learn Rust.

If I bring this up again, as you could see, if I close this window, the app doesn't go away but it stays sort of in the background. That's exactly what this code did. Let's quit this for good and move on to the Tauri version of this.

Here we are. Tauri version of this. Oh, do I have uncommitted changes? Oh, okay, okay. I just changed the app name. Sorry. No, cool. So if you are following along using Tauri, then you navigate to the first folder and you will notice this looks somewhat different, right? This, where's my HTML file? And the HTML file is in the source folder. So again, stylesheet HTML, we know this, right? This is the same. So one thing we already learned, all of these use essentially the same sort of web layer, right? There's no difference in the web layer as we expected, right? Because this is about building apps using web technology. The web technology part of this does not change. This is very much, you know, the same. But, the difference with Tauri apps is the source Tauri folder. And this is where, I need to explain a bit, because I don't assume that you are familiar with Tauri at all. The way Tauri works, as opposed to say the way Electron works, where Electron has a, you know, comes distributed as a single binary, right? That's sort of the Electron binary and you tell the Electron binary to run your JavaScript. Very similar to how you tell Node.js to run your JavaScript, right? But the way Tauri works is, Tauri is just a Rust crate. And it's a Rust crate that you can pull into your sort of Rust project, and you can then build your own desktop app using that, right? You can tell the desktop app to load the file, you can do whatever. But the important bit is that you are still in control of the Rust binary, right? Since the Rust binary is compiled on your machine, you can really pick and choose what parts of the framework you want, what parts you need, and then you can also bring in optional parts to expand the functionality. And that all happens sort of at compiled times. So really, you only get what you need, because it is sort of compiled, right? It's not just a static binary, but it's sort of compiled into a binary. And then that gives you a few advantages, and that's sort of why we started the project in the first place. The advantages being that since you only get what you really need, Taori binaries are much, much, much, much smaller and also more secure, because we sort of don't read any JavaScript files from disk or anything like that. The JavaScript files are baked into your app at compile time. That's what we can do, since it's a compiled thing.

What I just opened, the main.js file, this is essentially our equivalent of the main.js file. So in electron, you had the main.js file, which was creating your main thread and then opening a new window, loading your HTML, and doing all that. And this is essentially the same thing. You can see we have a main function here because Rust needs that to run. And the first thing we do is, we create our Tauri app. And then, here on this line, we run our app. Right. And that's sort of it.

Another very important file in a Tauri project is this tauri.conf.json file. And this is again, actually very similar to what we saw with PWAs, right. Where we had the manifest.json file, where we described our app, where we set sort of a name and a few other sort of things. And the tauri.conf.json file really is the same thing. Just with more options and geared sort of towards TAU, right. So right here, again, we can give a sort of special name to our app, we can set the version of it. And we can set a whole bunch more options. And this is, you know, goes against to the point that I was making where you are probably looking for a desktop app framework, because you want to go beyond what the browser can give you, right. So this is very similar to your manifest.json file from PWAs, but it's going beyond what the browser can give you. So and this file sort of will be read at compile time and Tauri will make, you know, the right choices for you in the final binary based on the options you set here. So that's one thing where the Tauri working group has spent a lot of time. We have sort of seen that, you know, learning Rust is a challenge. Learning Rust is not easy. And, you know, if you have a great idea for an app, you probably want to get this app, you know, done and out the door as fast as possible. You don't really have time to learn Rust, you know. So we try to make sure – and I would sort of, you know, believe that we achieve this very well now – is that you don't need any Rust to build Tauri apps.

Configuring Apps and Focusing on Electron

Short description:

You can configure your app using a JSON file and then use JavaScript for everything else. Tauri apps produce a single binary that can be distributed independently. Any questions so far? Are you ready to move on? I will focus on the electron variant for the exercises.

This was sort of one of the main points we had in mind is that you should be able to configure your app using this JSON file and then do anything else using JavaScript if you don't want to touch any Rust. And that's sort of, I feel like, that's very important.

So yeah with that, again, we can start using pnpm, we can start our Tauri app and what it just did, it compiled sort of our app and now it ran our app. And ta-da boom! We have our app and it's running. And you know again similar to electron, but different to PWAs, so differences and similarities right, a Tauri app will produce one single binary at the end that you can just distribute independently of any browsers or anything like that.

So let's close this down and head back to our slides. Cool! So any questions so far? Did you get stuck on the Hello World examples? Did anything you know, fail to start, fail to compile? Or are we good? Do you feel good to move on? Maybe you know, we can get a bit of feedback in chat, so you're not feeling left behind. Okay, crickets in chat, all good. Very cool. Nice. So I will, I will be mostly focusing on the electron sort of variant of this, right? I will also show a bit of the of Taure. But since I, you know, I kind of assumed that you are sort of the electron variant of this is the easiest to grok. I will sort of use that to show off the exercises and to work on them. I think that makes sense.

Common Issues in App Development

Short description:

Building an app is more complicated than a hello world app. This section focuses on common issues and teaches you how to think about apps in a way that accounts for differences in web development. You can apply your existing skills to desktop app development.

So cool. Now we have a hello world up running. But now what? Right. So this is one thing that I see a lot and half felt a lot when I started my journey. Is that a lot of the frameworks and a lot of the documentation and a lot of tutorial, it's always focused on, Hey, this is how you get up. This is how you get started. This is how you set up. This is how you set up your app. This is a hello world. And then you follow that tutorial, right? You follow that guide and you're done. You have a running hello world, but then the host is always like, cool. Now you are equipped to build apps. Have fun, go out there and do stuff. But I always sat there and was like, yeah, but I still don't really know how to approach this, right? Like building an app is a lot more complicated. Like building a real app is a lot more complicated than building a hello world app. So where do I get started? What do I do? What are sort of things to look out for? How do I think about apps? And so I couldn't come up with a better name for this than common issues, but this section is, you know, and this is sort of what the exercises are gonna focus on. It's gonna teach you a bit about how to think about apps, right? How to think about apps in a way that you sort of see where things are, again, you know, where things are different to web development than you might be used to or, you know, are the same to the web development that you might be used to. So you can sort of take your existing skills, your existing knowledge and extrapolate out into desktop app development.

Understanding the Main and Render Threads

Short description:

The main thread and render thread model is important in app development. It allows for multi-threading in web browsers, improving stability and performance. In this section, we will explore a demo app that calculates Fibonacci numbers and demonstrates the limitations of single-threaded JavaScript execution. The app freezes during the calculation, preventing user interaction. This highlights the need for background processing and efficient algorithms when handling computationally intensive tasks.

The most important thing, and I feel like this is sort of the foundation of any app development, and it's becoming a lot more important in the web as well, you know, as I said, the browser vendors are constantly increasing the blurriness of the line between what's kind of a native app and what's a website, but the one main thing to think about when you are working on apps is the main thread and the render thread, and that is a sort of a model that has been introduced first, I believe by Chrome, and that's multi-threading sort of in your web browser, right, and the idea is that you have one thread that is sort of in control of your app, that's the one that starts the Windows, that's the one that does OS interactions, and then that main thread spawns a whole bunch of render threads and utility threads to actually then draw stuff on screen, right.

So in Chromium or Chrome, a render thread would be equivalent to a tab or a window, right, so each website's context would be running in its own render thread, right? And that has the nice sort of advantage of taking, you know, taking advantage of the multi-core machines that we now sort of all use, but also increases sort of the stability of your app. Right? So if, you know, if, in the case of Chrome, if one website has a bug, it doesn't take down the whole browser with it, but it just takes down that one thread, right? Same thing goes for your desktop app. If one window sort of has an issue or is experiencing, you know, problems, if it crashes, it doesn't take down the whole app with it. It just takes that one window with it and, you know, you can recover and move on and maybe, you know, do some error handling and then present a nice error message to the user or something like that. So the threading model is important for that reason, but the threading model is also important for a couple other reasons and, you know, we will now see why and this is where we will jump back into a few exercises. So we are now at checkpoint 2. So let's move over to that.

Calls this one, calls that one. And before we jump into the real code, let me tell you what sort of the demo app is doing that we're working on right now. And we had a cool nice little hello world app, but that's not really doing anything and you're not approaching any kind of limitations with that because you're just rendering an H1, right? So I thought what's sort of a very simple thing that does a bit more than just hello world, but will still prove a point, will still sort of showcase what I mean. And I kind of figured, okay, let's make a simple app that just calculates Fibonacci numbers. Right? So Fibonacci numbers, this number series where, you know, each number is sort of dependent on the two numbers that come before it. And that is sort of neat because there's the way to express this using recursion, which is not very efficient. And that's sort of the point, right? So this function right here is sort of the least efficient version to implement the Fibonacci algorithm. And that is so that I can show you a bit of a performance problem down the line. And you can now, if that's the fact that this is the most unperformance version of this algorithm, if that kind of triggers you, then feel free to refactor this. There are couple variants on this that are more performant. But I wanted to focus on this app as a simple demo. And so what this app does, I can enter a number, and it will give me the first n, depending on the number that I entered, numbers of the Fibonacci sequence. So 0, 1, 1, 2, 3, 5, 8. I can bump this up, I can go to 10, I can go to 25, and you can see the numbers get really big really fast. And there, if you bump up this number really high, let's say 40, you will notice something interesting. So I clicked on 40, and now I cannot... Okay, I got it. Let's do 50, you know, let's do 50, calculate... And so, you can see now that while this is computing, I cannot interact with the page anymore. I can still sort of move it around, but hovering on this button does nothing. Scrolling still works, because that's, you know, not dependent on any HTML, but I cannot click this input field, I cannot change it. This app, for all intents and purposes, is completely frozen right now. So, this is nothing, like, there's nothing I can do with this app anymore right now. And that is because right now this calculation is done single threaded, right? So, this is a term you're probably familiar with. If not, let me, you know, quickly explain this. JavaScript, by default, runs in a single thread, which means that there will be only ever one instruction at a time, executed. There will only be one thing happening at a time, even though your machine, your computer, can, with multiple cores that it has, execute some code at the same time. It can do multiple things at the same time. And what's worse is with this implementation that we have chosen right here, this will never yield back to the main thread until it's finished. It's actually finished calculating all the 51st Fibonacci numbers. And that's bad, because in this world, where we only have a single thread to do anything, this thread is also responsible of calculating the background color of this button. Letting me interact with this input element, rendering these two buttons I just had here previously, where I could increment and decrement the number. It's responsible of making this interactive. All this is now sort of frozen because instead of handling any user input right now, what we are doing is we are calculating like incredibly large numbers and we're doing that very inefficiently. Not in the background, but in the foreground instead of handling user input, we're sort of crunching numbers. And that is sort of leading to a bit of an issue. And you can see, while I still can resize it, I can not do anything else. And it's still going, right? This is still happening. And sure, Fibonacci numbers and calculating that might be a bit of a contrived example, might be a bit stupid, you might say. But this could be also like, you know, decoding a video, or encoding a video, or doing anything that requires sort of heavy processing. And yeah, let me close this, so it actually stops. And that's sort of, you know, something to look out for.

Synchronizing App State Across Windows

Short description:

In the third version of the app, a new window button is added to simulate the functionality of opening multiple instances of the app. However, the app state is not synchronized across windows, which can be problematic in real-world scenarios. This emphasizes the importance of considering threads when developing an app.

And similarly, you know, if we now navigate to the third checkpoint, and we actually also move over to the third checkpoint, what this, you know, third version version of the app added is I added a button at the end right here. So I have here in the HTML, I added a new button called new window button, and that button just, you know, starts up a new window that spins up a new window that renders the same content, right, just like creating a second window of this app or a third or a fourth. And this, you know, is sort of resembling what you might find in any real world app, right, because chances are your app is not confined to a single window. And if it is, you might still want to be able to open multiple instances of it, you know, to have, you know, a side by side view or something. And we will notice something else. And that is, you know, if we calculate the Fibonacci numbers here and we now open a new window, this is not, like, this is not synced anymore, right? This is like out of sync now. This is not reflecting the app state in one window in this window. If I do, you know, if I calculate 10 here and even if I, you know, go to view and reload, this is not synced. This is not carried across windows, which is a shame. You know, in this sort of, again, in this contrived example, sorry, in this contrived example, this might seem a bit pointless. But, say, you know, again, you're working on an app which is sort of like a workstation app, right, then it would be very helpful if you have a settings window, right, and you sort of change some state in that settings window, that is then reflected in the main window, right? That's the whole point of settings, like that. If I jump back to the slides, this is like the second big thing why you should really think about threads, like, consciously when you're working on the app, right? And let me skip these.

Separation of Concerns and Refactoring

Short description:

Each threat in the system should be responsible for a different thing. The main threat is in control of the app, while the render threat is for rendering and receiving user input. The state is owned by the main threat and synchronized between all render threats. Think of render and main threats as client-server, where the server owns the state and the client is responsible for rendering and receiving input. In checkpoint 3, we will refactor the code to prevent the app from freezing on the main thread. Take a minute to think about how we can take advantage of multiple threads. I will demonstrate the solution using the Tauri version of the app.

And that's like what, you know, this technical term called separation of concerns comes in, right? And that is, I have all these sort of threats in my system. And each threat, you know, should be responsible for a different thing. And, or should be responsible for its own thing. And it should sort of be specialized in doing what it does.

And if we go back to sort of this drawing, right, we have the main threat. And we have the render threats. And for each render threat, like you might have many, right? So each render threat is essentially a very lightweight one with the main threat, as the name sort of suggests, being the main one. And I really, you know, I really want to hammer this home and we will hammer this home even further with, you know, the exercises now.

But the main threat, that's sort of what's in control of your app, right? So the render threat, as the name suggests, is for rendering and receiving user input. That is, you know, I type something in, it draws that to screen. I enter some input and click submit and it gives me sort of feedback. And then, you know, there's some state in my app, the state gets rendered and that's sort of the render threats responsibility. But it, importantly, it does not own the state, right? The state is owned by the main threat. It's sort of kept in the main threat. It should, anyway, it should be kept in the main threat where it could then, you know, first of all, protected from malicious sort of users interacting with your app, but it's also synchronized between all of the render threats, all of the windows.

And, you know, the other point is, you already know this, like think about render threats and main threats, not as render and main threats, but think of them as client server, right? Think of them as webpages and your server. The webpage does not own the state, right? If I have sort of a server where a user can log in, I'm keeping sort of the user details and all of the documents they have created or whatever, I'm keeping that on the server, right? The server is the one that owns the state and the client is merely responsible for sort of rendering that to the user, sort of displaying that in a nice way, and also sort of receiving user input to then manipulate that state on the server. And this is really how you should think about render versus main threat, right? So your main threat is the one that's responsible for working, uh... Sorry, no, no no. The main threat is the one that's responsible for the state and for interacting with the operating system, for doing the privileged things. And the render threat is for rendering. And with that point, let's jump into checkpoint 3 and this is where I want you to follow along if you want to. This is sort of the best opportunity for you to really play around now. And what I want you to do is, how can we refactor this code. How can we refactor the app of checkpoint 3? How can we refactor it so that we get this logic of the main threat. How can we make it so that when we click the button, let's go back to checkpoint three, how can we make it so that when we click the button and calculate something that the app does not freeze. This is the first problem that we will tackle. And maybe you can think about this for a second for yourself. And then I will also show you how I did it. And the way how I did it will also be the one that's in checkpoint four. So if you want to peek ahead or you just want to know how this is going to end up, you can do that as well. But I would encourage you to take a minute. Think about this I would encourage you to take a minute. Think about this, how would you make it so that this is not happening on the main thread? How can we take advantage of the fact that we have multiple threads running? How can we use that to our advantage? I'm gonna pause here for a second. I wanna do that. Have a play around with the code if you want to. And I'm gonna open up the TORI version of this. In the meantime, it's a bit easier in my opinion to demonstrate. Okay, just to show you, now if I click this in the TORI app, it freezes the same way. Now I cannot click, I cannot create a new window, I cannot do anything. This is completely frozen. So how can we move this off of the main thread? Close this down. Open this back up. Boom okay. Cool. Nice. Just sort of a quick question. Are you fine with me going ahead? I will, you know, I would, I'm not. You know. I want to leave you enough time to sort of play with this on your own pace, but also, I would just sort of jump into showing this off now, or sort of showing off my solution.

Moving Fibonacci Function to Main Thread

Short description:

In the case of Tauri, the concept of a main thread and render thread is more pronounced. We want to unblock the render thread so it can process user input. We move the Fibonacci function from the render thread to the main thread, adapting the syntax from JavaScript to Rust.

If you, I don't get any sort of cries. If no one's crying out for me to stop, then I will show you how I did it. Cool. Yeah. So in the case of Tauri, which I feel like in Tauri sort of this whole concept of a main threat and the render threat is a lot more pronounced. It's a lot more present. So I'm going to show this off in sort of a Tauri app. So, don't fear. This will involve a light bit of rust, but not a lot. Don't worry.

I will explain. So, first things first, right? We have our main and our render thread. So we want to unblock the render thread, right? So it can continue to process user input. And I feel like in this way right here it's also very easy to sort of visualize, right? Because we have the index.html file that represents in code, right? That represents our render thread. Then we have the file, which, you know in sort of source code terms represents our main thread. And so what we do essentially, right? What we do is let's take this function, let's take the Fibonacci function and let's, you know, cut it out of our render thread and let's bring it to our main thread. And you know, of course, let's get rid of this. Of course JavaScript syntax is not valid Rust syntax. So what we have to do is, you know, the function keyword is different, but then we have to give this a type and we will make this an un-sized integer with 64 bytes of precision.

Rewriting JavaScript Function in Rust

Short description:

We just rewrote a JavaScript function in Rust, and this will be like a hundred times faster. This might be exaggerated, but this will be now magically faster, which is crazy if we think about it, was really easy, wasn't it?

So a relatively large integer, right? Because we know that Fibonacci numbers will get really large. And this function takes in a big integer and it returns a big integer, right? And this end syntax to create a big ends as in JavaScript is not a thing in Rust, so we get rid of that. We get rid of this. And return statements in Rust can be implicit. So if you think about it, this creates a block and this creates a block and whatever sort of is the last statement of that block will get returned immediately, like implicitly. So it's similar like inline documentation for the win. return at the end will return that value from the function. But since these are the last expressions, sorry, let me bring this up. Since it's the last expression, the last expression will be returned even if you omit the return keyword. Just because Rust can figure that out for you automatically. So to clean this up a bit, it's optional, but let's get rid of the return and boom, we have just taken the JavaScript function and rewritten it in Rust. Yay, first Rust rewrite of our career. Maybe. I don't know if you are not. You might be very experienced Rust developers, in which case, hello, good to have you here. But if this is your first time looking at Rust code, it is this easy, right, this is all. We just rewrote a JavaScript function in Rust, and this will be like a hundred times faster. I actually have not measured it. This might be exaggerated, but this will be now magically faster, which is crazy if we think about it, was really easy, wasn't it?

Refactoring the Fibonacci Calculation

Short description:

We call the Fibonacci function for the elements up until the entered number. The resulting numbers are pushed into an array and rendered using replace children. We want to get rid of the blocking loop and return an array of numbers. We create a new function in Rust called calculate, which takes a limit parameter and returns an array of numbers. Tauri enables Rust functions to be available in the JavaScript frontend. We use the invoke feature in Tauri to call the Rust functionality on the main thread. Finally, we refactor the HTML creation code to use the array of numbers.

Sorry if I'm jumping ahead, the way that we did this, by the way, I realized I jumped, I jumped ahead of it. The way that we did this is that we call the Fibonacci function, for all the sort of elements up until the number we enter. So if we want to get the first six elements of the Fibonacci sequence, we call the Fibonacci function six times with 0, 1, 2, 3, 4, 5. And then whatever sort of the resulting number of the Fibonacci function is, we take that as the text content of a list item we created, we push that into an array of now sort of HTML DOM nodes, right, and then finally we sort of render them out using replace children. And that will make it so that all of our list items will appear in this HTML parent node. Sorry, I realized I jumped ahead. But we also want to get rid of this loop, right, because also this loop is blocking our render thread, right? This loop is also making it so that we have to call this a whole couple of times. So what would be ideal is if we could just get back an array of numbers, right? So let's take this out as well. Oh, let's copy it. And we bring this to our main thread. Let's create a new function called calculate because I'm incredibly, you know, uncreative. Which takes in a function sort of a parameter called limit. Again, let's make this a number and returns a vec of numbers. And in Rust land, a vec is just an array in JavaScript, right? So different word for the same thing. And let's refactor this for loop into a Rust for loop. So what this does, if we parse this very verbose syntax, this basically initializes i to 0. So from 0 to whatever the number is, that is in limit, we will run this loop and Rust has a very sort of elegant way of doing this. So the way we do it in Rust is we say, from zero to limit, limit, and then we go here and say for i in zero to the limit. And we create sort of new curly braces, and this now sort of this loop will now do the same thing as down here. And we, let's comment this out. We are not rendering HTML directly, right? But what we do is we create a new array. Let's call this out. And if you're sort of a Rust person you will now hate me for calling it an array. But essentially, you know, we create a new array. And what we do is we push the output of the Fibonacci number. So of i, we push that into the array. And lastly, what we do is we take that array and we return it. Boom. Simple as this. Now, Rust, sorry, now, Tauri has a sort of very, has a special thing that makes your Rust functions available to the JavaScript front-end, right? And what we do to enable that is we add this, sorry, we add this special bit of syntax on top of our Rust function. And this will take care of all the sort of binding magic. This will make it available in the JavaScript frontend. And lastly, we just have to tell people, we have to tell Tauri about the fact that we want to actually, you know, actually, actually expose this to the JavaScript frontend. And we do that by adding it to this line here. And with that, we now have our Rust code done. So we can, you know, let's jump back to the JavaScript and actually now make use of the things we just did. So let's comment this out. Oh, sorry. Let's comment this out and let's use a Towery feature called invoke. So we now, we exposed this, right? To the JavaScript, but now we need some way of calling this from JavaScript, right? And the way this works in Towery is import this property on the global object called, oh, sorry, invoke. Right, and as the name suggests, invoke allows you to invoke rust functionality on the main thread. On sort of, you know, on the background threads. So, you know, let's do, like, and can't type and speak. Let's use this, right? So, values await is equal to await invoke and then we have to give the method the function name that we defined here as the function to invoke and we give it the limit. Bring this back up. We give it the limit as the argument. Make this function asynchronous. Boom! This is it. Lastly, let's refactor this whole HTML creation code so that we take this array. This is now an array of numbers.

Moving Workload and Thread Synchronization

Short description:

We moved the workload off the render thread and unblocked the UI by converting an array of numbers into an array of DOM nodes. The Rust language excels at handling computationally intensive tasks, such as crunching numbers. We addressed the issue of thread synchronization by creating a struct in Rust to hold the array of values. This allowed us to move the state of our app from the render thread to the main thread, resulting in synchronized windows. Although the demo app requires a reload to update, it is possible to implement live updates by emitting events from the main thread to the render thread.

This is now an array of numbers. Let's take this array of numbers and turn it into an array of DOM nodes. The way we do that is we map this array, map, oh, sorry. And then we do this, we do this, the to string, see, we replaced, you know, we replaced the JavaScript code to call this now. And lastly, we just return n, because JavaScript does not have the nice feature of Rust where we can just omit this line.

Cool. Lastly, render it out. And that's it. This is all the code, you know, required to move this workload off of the render thread and unblock our UI. So, let's rerun this. Let's see if I made any glaring mistakes. Does this still work? Boom. Very nice. And now, you know, let's test this out proper. Let's run this and say, 40. And you can see first of all, this was really fast. Actually as fast so that I cannot really showcase this to you. Okay, maybe, can I inspect element sources? Yeah, cool. Nice. Well, you know, I can still, this is a bit hard to showcase, but I can still interact with this element right here even though it's sort of, you know, running and crunching a whole bunch of numbers in the background. And that's really also what Rust is very good at, right? Crunching a lot of numbers. And so, that's one of the reasons, as well, why I chose to sort of, you know, demonstrate this with Towers.

But with that, let's head... no, let's not head back to the slides. Let's tackle the next thing. And I'm not gonna... for this, I'm not gonna, you know, do the whole thing and code this with you as well. But let me sort of talk to you how we address the second issue. The second issue was that now we have a cool app, you know, it stays responsive, it's making use of the threads we have, it's sort of making use of this main thread versus render thread paradigm, but if I create a new window, this is still not synchronized, right? So how do we sort of synchronize our threads? How do we structure our app so we get the state out of this thread and into sort of the main threads so it can be synchronized? And in Tari that is equally as simple, and let me just jump to number five.

Okay, so what we do essentially is, remember our Fibonacci function? Yeah, so what we essentially did is we created a struct and the struct is like an object in RUst, right? And this object will hold our array of values. So now, we move the array of values from JavaScript into Rust. So we moved it from the render thread into our main thread, into our Rust thread. And similar, you know, Calculate, we had to do a bit more to sort of, you know, lock our access to that sort of object because it's Rust. Don't worry about that. What I want you to take away from this is that we now move this value, like we moved this value, we moved the state of our app which this state is very simple, right? This state is just an object, sort of just an array of numbers. We moved this array of numbers so it does not live in the render thread anymore. It's, you know, not living a separate, having, not having a separate copy of the state in every render thread, but we have one which lives in our main thread. And I can show you what that effect has or what the effect of that is by running checkpoint 5, right? Give it a quick second.

Okay, now we have a window and let's, you know, let's do something not as crazy as 67 Fibonacci numbers, but let's do something like 20. Calculate, boom, real fast. But now we open a new window. Oh, see that. Yeah, it got the Fibonacci numbers from the main thread and displayed them sort of immediately. Right. And if we now sort of, let's say 10, you now updated this list and you can see this didn't update, but if we reload it updates. And that's because I sort of cheaped out on on this sort of demo because I didn't want it to get any more complicated than it already is. You could now very easily also add sort of life updates. Right. You could emit events from the main thread to the render thread to tell it, hey, you know, the state just updated. But in this case, sort of, we need a reload to sort of get that info.

Managing State and Security in Desktop Apps

Short description:

The render thread is the first line of defense against malicious actors. The main thread is responsible for managing the state and interacting with the operating system. The render thread can request actions from the main thread, which validates and sanitizes the requests. If a compromised front end attempts a malicious request, the main thread can prevent it from reaching the operating system and take appropriate actions. Building desktop apps is similar to building server-client apps, requiring a repurposing of knowledge. The concept of a native feeling UI is subjective and varies across operating systems. There is no clear distinction between a native UI and a non-native UI.

But the point is, this state does not live. So in the Rent a threat anymore. Right. This state is taken off of the render threat into a different one into the main thread that is responsible for managing the state. And there's one there's one other very good reason you would want that, and that is security. And this is something that's very near and dear to my heart. This is something that's very important to me. Security in apps is something that's often overlooked, and that's, you know, especially with web technology, that's something we are quite used to, right. Like web browsers nowadays are very good at sandboxing our sites. And even if, you know, someone builds a malicious website, chances that that malicious website will like seriously infect my machine and like, you know, wipe all my files, encrypt everything and, you know, blackmail me for it. It's like a small company worth of bit of bitcoins. That's very small. But with desktop apps, right, these frameworks give you a lot of access to the machine, because that's what you want, right? You really want to be able to interface with the machine with your desktop app. But with all that added power also comes, of course, added risk, right? If I am able to exploit your desktop app, I can do a whole bunch more damage than what I could do with the website. And here comes, you know, again, the way to think about apps using this main thread and the render thread comes into play again, because the render thread should always be seen as unprivileged, right? This is sort of your first line of defense against any malicious actor. Whereas the render thread, that's sort of where you are XR, right? That's sort of your precious treasure is kept in the main thread and it's protected just like with a server inclined where you are sort of your important data that's kept on this, right? You're not sending any secrets to the browser, to the client. And in this setup, I want you to think about this again, you know, like serving clients, the render thread can ask the main thread to perform actions on its behalf, right? Because the render thread often very much sandboxed as well, like a website. And so whatever the render thread wants to do with the operating system, like reading files, like notifications, like screen sharing, like sound, whatever, right? And all of those situations, the render thread has to ask the main thread to do it on sort of the render thread's behalf. And we can see this sort of in a fun little way. I try to like visualize this when I tell people how I did it, and that is using people, using stigmas in this case. Because it's a lot like with how you would secure anything in the real world, right? If I have a bank, I'm not allowed to walk straight into the vault and take out the money I want. I have to ask sort of the person at the counter, hey, can you give me $10 or whatever? And so, in this analogy, the render thread might say, hey, main thread, can you open this file for me? And then the main thread can inspects that request, looks at the render thread, validates the arguments and then reads the file from the operating system. The operating system then gives the file back and we hand that file back to the front end. And now, we can take this file and we can render it out, we can do whatever with it, right? But what happens if a malicious user compromises our front end, right? Again, because layers of security, the render thread is our first line of sort of defense. And what happens if that first line of defense gets compromised? Well, in the case of a compromised front end, we are going to request a file and in this case, a malicious actor wants to read the passwords of her machine. So it tells the main thread, hey can you read this file? And the main thread will inspect that request, will inspect the front end and will determine, hell no, I'm not gonna give you out all the passwords of the machine, that's not none of your business, that's not what your purpose is. If the uncompromised front end for example, is supposed to just render out Fibonacci numbers, what business does it have of reading the passwords, that does not make any sense. So we can see that in this case, in the best case, in the best security case, a compromised front end, that request never even touched the operating system. In that case, what the main thread would do if it determines that request to not only be invalid but also to be malicious, it could terminate the render thread, it could delete all the files, it could prompt the user in error and be like, this looks like your app got infected. And that is very important because as soon as you get down deeper into the more privileged layers, the more damage a malicious user can do. And so to sort of reiterate that point, the render thread treated as unprivileged, treated as malicious. And the main threat is the one that's in sort of in control of the resources that's in control of interacting with the operating system. And it should validate and sanitize any requests that come through to it. But, and that's also very important. Again, the theme of differences, but also similarities, you already know this, right? This is the same way that we build server and client application, right? The server should always validate and sanitize any requests. So you already know all of this and this is the important part. But building desktop apps is a lot, and that's sort of very important to me, is a lot like building server client apps. You just have to sort of repurpose your knowledge, right? And sort of rethink all those building blocks you have in your head of what a server does and what sort of the website does. Take these building blocks and you need to sort of shuffle them around a bit and boom, you already know how to build a desktop app. So that's very important for any desktop app. One of the other things where people will often say, you're up, that's not really native, that's not, it's still a website, yada, yada, yada is native feeling UI. And that is you know, calling back to the history lesson we had right at the start of the talk. What, you know, native feeling, what does it even mean? And that's a good question because I don't know, and so like, no one knows. And that is the important part. Like, this is a, sort of, this is a tricky and very difficult area and and that is because, you know, UI is a lot of like choices, right? And it's different on each operating system. It's different depending on the Linux frontend that you use. And there's also no real, sort of, clear distinction what is a native UI and what is not. For example, and that's, sort of, like, the fun fact I always like to tell people here is that the macOS settings app, right? If you are on macOS you are familiar with it. If not, also might be familiar with it. The settings app.

Native vs. Web and Virtual Machines

Short description:

A lot of the pages and widgets in the app are not native, but webviews. The line between native and web is blurry, but you can feel it. Setting up virtual machines is a great way to test your app on different operating systems.

A lot of, sort of, the pages in that app and a lot of the widgets that you see actually are not native. They are just webviews. They are just, sort of, embedded websites and that's crazy, right? Because, like, if I, you know, if I hadn't told you that, you would have never noticed. And that sort of goes back to the history lesson, right? The line is very blurry. And it's very, sort of, hard to define what is native and what is not. But you will feel it. And that's, sort of, why I say it's tricky, right? It's difficult, sort of. I kind of assume you, most of you, are developers and, sort of, certainly me as a developer. I like certainty. This definitely being the case. I like it when things are clear. But with UI and especially native feeling, whatever it means, things are very much not clear. But one tip I like to give you, and that's one thing that I would encourage you to really, sort of, investigate. And I sort of made one video on my YouTube channel, and I'm planning to make a lot more, is to set up virtual machines. Virtual machines are super great, because you can have all of the operating systems that you are interested in, on your machine locally, right? Because me, if I wanted to test my app on Windows, on Linux, couple of distros on Linux, but then also maybe OpenBSD, I would need to have this stack, this big of a stack of laptops, and switch between laptops. No one's got time for that. So, virtual machines, great tool. Great, great tool, use them. That's my big recommendation in this field.

UI Design, Packaging, and Distribution

Short description:

UI design is a complex profession, and hiring a designer is crucial for creating a native feel in your app. However, the industry still lacks a silver bullet solution for UI design. If you have ideas to simplify the process, consider building a library or sharing your thoughts. Packaging and distribution are essential for desktop apps, involving building, packaging, code signing, and distribution. Code signing increases user trust but can be tedious and costly. Finally, distribution is the key to getting your app to users and enabling them to interact with it.

I'm gonna skip the example and change to this slide, right? The second tip I have in regards to UI, and I'm looking at everyone here that is always drawn to the same web developer disease that certainly I started out with, where I was like, oh yeah, I'm a web developer, I also know CSS. I can just design this, I can code it up, I can implement the functionality, I can make this work, I can build a full app myself, but, you know, turns out design especially UI design, that is a profession for a reason. People study that for a reason, because it's really complicated, because it's really hard, right. And so, if you are, you know, serious about your app, and I probably don't need to tell you that, but hire a designer, especially if you're sort of trying to nail operating system specific UI and interactions, and you want to feel as native as possible, hire a designer, hire someone who really knows what they are doing. But again, this is an unsolved problem, right. This is something that we as an industry don't yet have a silver bullet here. So, if you have a great idea of, you know, simplifying that, because whenever you see something that's an unsolved problem, whenever you see something like that, that is an opportunity, right. We can improve as an industry. We can build tools that make this easier. And so, if you have an idea, if you have a thought in mind, if you know sort of a way that we could simplify the process of building UIs, and you want to do the industry a solid, then maybe consider building a library. Certainly, tell people about your idea because this, again, is an unsolved problem. This is something where we, as an industry, still have to improve, which is also very exciting. And lastly, and this is sort of the one other big thing in desktop apps, packaging and distribution. What is packaging and distribution? What is that? And packaging and distribution, that's really where apps are different from websites because websites are deployed over HTTP. They are loaded live, if you will, whereas apps, they are sort of distributed ahead of time in a variety of different packaging. On Android, you would use APK, if you're familiar with that. On macOS, you would use or.dmg. On Windows, it's MSI. Things like that. To prepare your app that was a website and a binary, to prepare that for distribution on your website or the app store, that usually requires a four step process.

That four step process consists of building, which is the process of taking your HTML, your JavaScript, your CSS, but in the case of for example, Taori, then also your Rust code, compiling all that, transpiling, it run a package, like, you're taking that up, creating files out of that in a format where that is now executable. It's not source code anymore. It's something you can double click and it will run. And depending on your framework, that step might be optional. That step might take longer. That step might be nonexistent at all. Because with PWAs, for example, there is no building step at all, unless you use a front end framework that requires a building step like Svelte or Vue or whatever. But with Taori, for example, again, differences and similarities. With Taori, this is very much pronounced because the building step, that is where the magic happens. But cool, now you have your executable, what now? Second step, that is packaging. And packaging, very much similar to the previous step of building, but packaging now takes your executable and a whole bunch of metadata, and stuff like icons, for example, and packages them up in one of many, many possible app formats, right? This is the step where out of an EXE on Windows, you create an MSI, or on Mac OS, you create out of an ELF executable, you turn that into an app with an icon. And now, this is already feeling much, much more like a real app than just any regular executable. Very important step. This is also where you set your description for your app and things like that.

Third step, often overlooked, but if you really get down into the weeds of it, this is sort of often the most painful step, and that is code sighing. And this is required from Mac OS, so Mac OS will not, if they're of course like developer workarounds, but like, in the common case, will not even install your app if it's not code-signed. And required-ish for Windows, if you are sort of familiar with this like warning prompt, this app might contain malware because it could not have, it could not have been sort of checked and it's not from an authenticated developer. This prompt on Windows, that also goes away if you code-sign your app. So this, you know, this step is really important if you want to increase the trust with your users, which, you know, for open source apps, not as important. Also code sign often costs money. So on macOS, you have to sign up for the developer program. On Windows, you have to sort of buy this certificate. And this is a bit, this step, you know, it's a bit annoying. This step is a bit tedious, cumbersome, but it essentially, what it does is it makes sure that the app users downloading is coming from the developer that they intended, right? So using that, you really prove that you are the one that built this app. That this binary that a user downloads is actually coming from you and not from a malicious party. So it's really, you know, it's really important actually. I'm now speaking of coming from you versus someone else. Last step, distribution. Now you have your fully, you know, code science packaged build app. How do you take this and get it to your users? Because that's ultimately what counts, right? Getting this out to the world, getting this out the door to people for them to interact, interact, to use it.

Building and Distributing Desktop Apps

Short description:

During this workshop, we have learned about the client and server architecture, the main thread and render thread architecture, and how to apply this knowledge to build desktop apps. The goal is to make building desktop apps as easy as deploying a website. The working group of Tauri is focused on improving the build process, user experience, and distribution of desktop apps. They offer consulting and auditing services, as well as job opportunities. If you have any questions or need assistance, you can reach out to them through various channels.

And for that, you know, again, you can use a lot of different techniques and, and, you know, services. What I use a lot and what most people use and what I would also recommend you use is GitHub releases. But there's of course like AWS and all this like all these previous steps of like building and packaging and code signing. These are things that you can also automate in CI. So you do not have to think about them constantly as an individual developer. And that's sort of the step four of, you know, CI and distribution and getting your app out there. Also, you know, in your CI step, you would then submit it to Apple app stores or, or like Android stores, Windows store, things like that.

Cool. You know, so what if we, what if, what have we learned during our, during the, this workshop so far, and that is all right. You know, what I sort of would want you to take away, especially is very important, this client and server architecture, right? This main thread and render thread architecture. This you already know this, and you can apply this knowledge, especially sort of with focus on, you know, performance and security. You can use this knowledge to build desktop apps, right? Use multi-threading, use this to your advantage. And again, like, think about it as if you were building client and server apps. And yeah, again, you already know how to build an to build the desktop app. And that, to me, is very exciting and very empowering. You know, the web tech desktop app frameworks, they really democratize access to building desktop apps, and that's very cool.

And one other thing sort of, you know, that I at this point should mention, what I mentioned right at the beginning of my talk, the last, you know, few steps that I talked about with the bundling and things like that, we sort of as the working group of Tauri have, you know, thought about this a lot and we have spent a lot of time and energy sort of, you know, trying to make the build process nicer, trying to make the user experience nicer, and, you know, what, sort of, our latest, what our latest effort in this direction is is actually spinning up a company. And this company, you know, mentioned it right at the beginning, you know, the middle sort of category I wanted to focus on is distribution, and this is what we're building on right now, is making what I just talked about with assigning and distribution and building a packaging and CI making this very easy. Because right at the start as I mentioned, I truly believe that building desktop apps should be as easy as deploying a website, and we're sort of working on that. We're not there yet, you know, you can follow us if you want to sort of be kept in the loop. But that's sort of where our heart is, and this is where, you know, we think that improving the experience for desktop apps, and apps in general now, that's sort of where we spend our time to really make that area really good. And also, you know, if anything, that was interesting during the last, what is it now, two hours, you know, if you enjoyed that, if you got inspired, if you sort of want to build an app as well now, and you want to get help, you know, similar to what I talked about, we also do consulting, we do auditing. So, if you or your company is looking to build an app, we can teach you, we can help you, we can make sure your code is good, things like that. And lastly, I should also mention, we are hiring so if you want to work with us on this, you know, on improving apps, bringing cool, nice performant and small desktop apps to the people, get in touch, you know, click the link, message us on Twitter. And with that, thanks so much. Oh yes, and Lorenz also just mentioned in chat, if you want to enter our giveaway also do that. We should tell more people about it. Cool, well, with that, we are not quite over the two hour mark, very close to it and I would say I open this for questions now, so any questions, anything? Also of course you can always jump into the discord server, or message me, or mention CrabNebula on Twitter or anything, if you have any questions at a later point in time. If not then, you know, thanks so much for being here and I would call this a wrap now.

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

Tauri Foundations and Futures
JSNation 2022JSNation 2022
22 min
Tauri Foundations and Futures
Tauri is a rust-based, security-first, open-source application construction framework built on the philosophy of shipping better projects without compromising on our climate goals. This talk will introduce key components and benchmarks of the stable release of the fully-audited framework. Further it will discuss its future as a means of not only delivering desktop and mobile apps, but also its mission of backfitting servo in order to make a fully fledged all-platform webview provider. Finally, we will present our award for "2022's most secure modern web-framework" in the context of webview-based Tauri apps.
Observability Matters: Enhancing Performance of our Node Application with OpenTelemetry
Node Congress 2024Node Congress 2024
7 min
Observability Matters: Enhancing Performance of our Node Application with OpenTelemetry
Have you ever considered that when we encounter terms like observability and reliability, our initial instinct is usually to attribute them solely to SRE concerns? Yet, upon closer examination, one may realize that actually implementing observability is, in essence, more aligned with the domain of developers. After all, developers are the ones who directly implement the actual logic into our existing codebase, and who better to understand and debug their code than the developers themselves? Through this session, we will emphasize on understanding the importance of observability specifically from a developer's perspective. Let's explore some best practices that help us effectively debug the performance of our Node application and how the inclusion of open source frameworks like OpenTelemetry could be beneficial to us.