 excited to be here today and I hope that you are as well. I'm going to be speaking to you about building better desktop applications with Ember. My name is Estelle DeBlois and you can follow me on Twitter at EdeBlois. I'm also on GitHub at BRZ Pegasus. So I started working with Ember about two years ago, over two years ago now, and I have to say that it's been such an incredible experience to see how much Ember has grown within that time period. There have been so many new and exciting features that have emerged from the framework and you know you can't deny that Ember has been moving really fast. And now the first beta for 2.0 was just released. So that's really awesome. But there's something else that I find as equally important and crucial to Ember's growth and it's the community itself. This is a picture that was taken at last year's Wicked Good Ember conference. I was standing right there and right behind me and also around you may recognize all the happy and friendly faces that made up Darkyard. And we didn't know each other then, but this year, this conference, I have to say that I feel fortunate to be able to call myself a Darkyarder. So I joined the team later that summer. If you don't already know, Darkyard is a design and software consultancy right in Boston. And so we build web apps, lots of web apps using Ember because we believe that it's the best framework for creating rich, powerful and beautiful user experiences. And over the last year, we've also had the opportunity to build for the native desktop. And that's what this talk is all about. Today, I want to give you a taste of what it's like to build a number app for that environment. There are five sections total. There's going to be a demo in each section. And hopefully you will walk away with a good understanding of the sort of challenges that are present in that environment. And you'll see how we were able to leverage Ember and Ember CLI to help us along the way. So let's dive in. If someone came to you today and asked you to build a native desktop app, what would you choose to build it with? I have written some desktop apps for my part in the past. And they were all written with Java swing. This must have been like seven years ago or so. But today, the landscape has changed quite a bit. And we have a lot of new options available to choose from. This is a screenshot of a desktop application that you may recognize. You may use this on a daily basis for your coding. This is the Atom Editor. It was developed by GitHub. And while it may not be apparent to you, this was written with CoffeeScript and styled with CSS. And what made it possible was a framework called Electron. It's an open source project also developed and maintained by the GitHub team. And it lets you build cross-platform desktop applications with web technologies. Here's another example of a desktop application also written with web technologies. This is called PopQuantime. And you can do things like watch trailers. You can download movies, TV shows from Torrents. And it uses a runtime called NWJS, which you may have heard of in the past as the Node WebKit project. It's similar in concept to Electron, but it has been around much longer. And this is the technology that we ended up using in our project at Dockyard. And this is what I'm going to focus my talk on today. So NWJS is an application runtime that is built on top of Chromium and Node. And so when you build web pages, they get rendered in a Chromium window. There's no web server involved. Your pages are loaded from the local file system. And because NWJS also integrates Node into the browser, you also have access to all of the system-level functionality that Node provides. You also have access to a native UI library so that you can add application menus. You can add things to the system tray. You can even manipulate how you want the application window to be like. So you can do a lot of things. I'm going to show you quickly what an NWJS application works. And hopefully you'll get some more context for the rest of this talk. So when you get started with NWJS, the first thing you want to do is go to the website and download the pre-built binary. And there's one for different platforms. But what I found easier to do... So you just want to fix the over scan because it's leading to the sites. Just go to the display settings. Okay. Under scan right there. So just drag it all the way to the right. That didn't fix it. I can just move it to the side. All right. Can you guys see that? Can you read it in the back? Okay. So I mentioned that you can go to the website and download the pre-built binary. But I found it easier to just install the NWNPM package instead. It's not a Java's report of the runtime. It's simply a package that will detect what platform you're running on. And it'll download the binary for you. So I have done that ahead of time. So here's my NWNPM dependency. I also have a very simple index.html page, as well as a package.json file. This file is really important. NWJS will look for this when it launches. And it'll look specifically for a field called main, which I have on line three. The main field needs to point to an HTML page. And that's going to be the starting point of your desktop application. I also have some additional fields here. Window, toolbar, false. What I'm telling NWJS to do is to hide the toolbar when the window comes up. So window is one of many options that are available to you. You can specify in your package.json. So I'm just going to go ahead and launch it. Because I've installed the NWU dependency, I can now run NWU in the command line. And that's... Okay. So that's like... All right, so you get the desktop application running, and it loaded the HTML page inside of that window. So that's basically how it works. And right now there's nothing special, but you can see I've got some placeholders. Okay, so I've got some placeholders. I'm going to display the name of the current platform, as well as the name of the user of this machine. So I'm going to go back up to the index.html. So how would you go about displaying the name of the platform? If you're in Node, you have this global variable called process, which has all of that information. But because we're in NWJS, that process variable is available to us as well. So you can do things like process.platform. And it's going to be... By the way, I'm not torn-billiped, so I cannot do the magic, so just bear with me. Okay, so for the username, it's really... It's something that is read off of environment variables. And the process object also has that information. So you can do things like process.f, and in this case, we want user. So I'm going to restart this, and when that comes up, you'll see that the values have been detected. It's showing DAWIN for Mac, and then the RCFectors as the username. So there's something else that I want to show you. I mentioned earlier that you can do things with the native UI module. The way that you would require it in is by calling require nw.gui. It follows a very similar syntax as if you were to import node modules. So in this case, I'm going to just go ahead and do that. So I'm requiring nw.gui, and from there, you have access to different things. So we're going to try to get the current application window. And you have many methods that are available to you here. You can use when.... You can show the DevTools. You can resize, and I'm going to do... I'm going to instruct it to open the application in full screen now. And there you go. So a little cutoff, but you can see it's still working. So far, every time that I've had to make changes to this file, I had to restart the nw process all the time, and that can get really annoying. You don't want to have to do that during development all the time. So how about, given that we can access node modules within here, why not try and do require fs? If you're familiar with node, there's this fs module that lets you do things with the file system. It has a watch method that lets you watch a directory. So I'm telling it to now watch the current directory. And then how about when something changes, we do a simple location reload. And so now, if I were to run this... Whoops, actually, I'm going to remove the full screen thing. Just a bit. All right, relaunching. And I'm going to bring this to the side. I know it's cutoff, but you should still be able to see, get the idea. So up here, I'm going to just add some text. And then without doing anything else, you're going to see that that reloaded immediately. So very, very simple. And that's nw.js in a nutshell for you. Okay, so far, that was really, really... It was a very small chunk of code. Obviously, this is not how we would go about writing a real application. You would always want to think about how to properly structure your project, and you would have to make decisions around your application architecture. And you have views that you have to manage routes, models, states. If so, then maybe Ember can help. So let's take a look at how we can use Ember together with nw.js to build more powerful desktop applications. This is a typical Ember app that you would get if you were to create it with Ember CLI. And as we saw earlier, the first thing you would have to do is bring in the nw dependency, update the package.json to have this main field pointed at your index.html file in the disk folder. And now this thing is unique to Ember, but you want to go into the config environment.js file and modify the router location type, set it to hash, so that it doesn't try and use history. That's because we don't have any web server involved. So those three steps should get you in a good stage to now build and launch the application. So normally you would run Ember serve. But because we don't need the server part, we can just do Ember build. And you can still pass a watch option so that your application files can still continue and rebuild as you modify them during development. So we leave that process running, open a new terminal window, run nw, and that's going to launch the Ember app as a desktop app. And that's really simple. That was all we had to do, right? At least it seems like it's working. However, if you now try and do anything with Node or if you try and invoke the native UI module, you're going to get this error message. It's going to say, could not find module nw.gui. And that's because we're essentially in an environment where we've got browser code and we have Node code all together and certain things aren't going to play well together. So out of the box, nw.js defines this required function at the global scope. And you can use it like we saw earlier. If you specify the name of the module and the name is nw.gui, it's going to return you the native UI module. Otherwise it's going to call the required function that exists in Node. The problem is that when our Ember app loads, MSCLI's module loader also defines a required function at the global scope, which means that it essentially is overwriting whatever we had before. This required function now is used to load up AMD-compatible modules that you would write in your application with ES-2015 syntax. So there are several ways of dealing with this, but the simplest approach is really to get a reference of the required function that existed beforehand from nw.js, save it to a non-conflicting variable name, load the app, and then at the end redefine the required function to work with both module systems with a simple tri-catch. So obviously, I mean, I know it's not a ton of config, but it's still boilerplate that you would have to bring in to any new app that you want to build with nw.js. And whenever you find yourself doing a lot of copy-pacing, you know that it's time for an add-on. And so we made one. This add-on is called Ember CLI Node WebKit. It's an extraction of the work that we have put into the project at Dockyard. So I'm going to show you how the add-on can help you get started quickly with nw.js. Okay, so over here I have a... Let me just fix this window issue here. Okay, so I have a very bare-bone Ember app. This was created with Ember CLI. The only thing that I've done at this point is run the Ember install command to get the add-on in it. And that's about it. However, because I did that, I now have the nw dependency in the Node modules folder. The add-on took care of doing that. It also took care of adding the main entry that nw.js requires. The configuration for your router is also handled automatically, as well as everything that is required to work around the required naming conflict that we saw earlier. So all you have to do really is just install the add-on and then run your app. So instead of doing ember build and then open a new terminal and do nw, we're going to use a new command that is given to us by the add-on as well for convenience. You can do both things with just this one command, ember nw. This is going to build the application. And then as soon as the application is built, it's going to launch it in nw. And navigate back here. And here's our ember app. So this was really simple to do. Also, if you make changes to your application files during development, things will still continue to rebuild and you will see the changes reflected in this window without having to do anything else. So that concludes this demo. All right, so let's move on to development a little bit now that we saw how to really get started with this. So I don't know about you, but whenever I develop web apps, I like to have the DevTools open. I leave it open because I inspect everything from error messages to deprecation warnings, but I also use the Ember Inspector a lot. I find it so helpful. You can see so many things that are part of your app, like what state it's in. You can check all the records that have been loaded into the data store. Sometimes you look in there and you find certain things that should have been cleaned up, but they weren't. So essentially, this Ember Inspector is essential to my general development happiness. And when we started to develop the app in NWJS, it turns out that the Ember Inspector was not available in the DevTools in that environment. And in fact, none of your browsers are available to you as well. We're not dealing with Chrome. We're dealing with Chromium. This is just the basic stuff. So of course, we had to work around that. And so we realized in our project that even though we were building a desktop application, there were only certain parts that needed to do anything special with Node or the native UI. All of the other routes were not dependent on that. So there was no reason why we couldn't load those routes into Chrome or Firefox and do our development and debugging there for those times where we need to focus just on those routes in particular. So that's what we did. But we have to be able to detect which environment we're running in, because if we're not careful, we're going to run into issues when those things, when NWJS objects are undefined. We don't want the application to completely melt down. So to detect the environment, we can create a very simple module. Let's call it environmentJS. And then just put some flags that are related to just whether or not we're running in NWJS. So we could check for the presence of certain variables that we know would only be there if we're running in NWJS. And if they're not there, then we can set the isNWU flag to false. That way, we can use those values to determine whether we should be initializing the application with an application menu bar or maybe we should just return if we're not in the right environment or do something else. So since I'm mentioning menus, let's see how we can go about handling menu interactions. In NWJS, the way that you would create a menu item is by just doing a new GUI.menu item. This is an API that comes from the native UI module we saw earlier. So a menu item takes a label. It takes some keyboard shortcuts. And then it also takes a click handler. So this is where you define whatever you want to do when someone clicks on the menu. If this was a number component, then you would most likely do something like this, right? Send actions, specify the name of the action, let that bubble up to some route. Also, I want to specify that this syntax is changing in 2.0, but I'm not going to go there. So given that we're not in an amber component, the send action is not available to us. But we have something else that amber gives us, which is this mixing called amber-evented. Amber-evented gives us access to these three methods, trigger so that we can trigger a custom event. And then other objects can register and then unregister listeners for those custom events using on and off. Because it's a mix-in, we can easily mix it into an amber service because services can easily be injected into different parts of the application. So let's say that we are able to inject the service into wherever our menu code lives. We can now do trigger and then specify the name of the menu event that occurred. So other parts of the application can now respond to those menu events by using the same service and register listeners for them. If we want, we can also be a little bit more expressive with this by creating a menu events hash and then just list out all of the menu event handlers that we want. So this is following the same pattern that you have with the actions hash that you have in routes or components. And it just makes things a little bit more declarative. So I'm going to finish this section with a demo of a desktop application that can also run in the browser. Okay, so this was constructed with Ember CLI. It's using the Ember CLI Node WebKit add-on. So all I have to do is run Ember NW like you saw earlier in order to build this and launch it. So it's going to come up momentarily. And now we have this. It's basically, this is a simple markdown editor. You have an edit pane. You can just create markdown documents. You can preview them. There's also an application menu that lets you save this document to disk. And you can reload and all that, like reopen documents and edit them. So you get the idea. But now, if I, instead of running it with NWGS, I'm going to launch it in the browser with Ember SERV. Ember S is just a shortcut for the Ember SERV command. So I'm going to bring up a browser and go here. Let me blow it up a little bit. So it's the same markdown editor application. I can still edit markdown syntax. So the functionality is still there. The application is not broken, even though we no longer have access to the application menu and you no longer can save or open documents. But you can still, the application is still functional. All right. So I'm going to switch back to the slides. And we're going to move on directly into testing. So when it comes to testing, obviously doing a lot of the separation of concerns that you saw in those examples earlier really helps. Let's say if we keep with the same markdown editor example, if you have a document model that defends records that should be saved to disk and also reloaded from there, you can easily create a custom data adapter where you can override the find method and do all the things that deal with the file system stuff by requiring node modules. The problem is that now we've got the node code and the ember code and it's all mixed together and it makes it difficult to test this. So we could try instead to just extract all of that node heavy code into a service and in that way you can easily stub the service during test. So now we know how to test this. But still, how do we test the ember service that does all of this file-related stuff? So when we dug deeper into the NWJS documentation, we found out that besides main, you can also define, you can specify a node main field in your package.json and that would have to point to a node script that you want to execute on startup. This is usually used to run background tasks and whatnot but it actually worked out really well for us. We used it to essentially expose all of the node-related API through this single script that the ember app could then consume. And this is good because all of the node-related code can now live in node modules and you can write tests for those node modules the same way that you would write tests for any other node modules. So anything that gets exported from that script can then be accessed from the browser using process.main-module.exports. And then so we have access to the file service because it's available off of that export subject and we can even create computed aliases and everything to help us. So this is essentially the pattern that we adopted in our project at DuckYard and it worked out really well. But then a couple months ago somebody named Stephen Edward paged me on GitHub in the add-on repo and he said that he was working on this Azure Storage Explorer desktop app. Microsoft, so Stephen Edward works at Microsoft they've already got this product that does this that's available but it only works for Windows and he wanted to create a cross-platform solution so he decided to build it with Ember and he decided to build it with the Ember CLI Node WebKit add-on. However, he wanted to use a test-driven development approach to build this and unfortunately at that time the add-on did not have any kind of support or TDD or integration test. So we brainstormed a little bit and after much collaboration and many contributions on its part a new command was born. Today if you were to install the add-on you would have this new command available to you and it allows you to run tests in a NWJS environment. So I will show you how that works but let me just quickly explain the steps. So when you run the NW test command it will invoke Testim and it will tell Testim to launch the NW process. Typically when you run Ember tests in your Ember app your launchers would be things like PhenomJS or Chrome but we can configure Testim to run any kind of process that we want. So tests are now going to run inside of the NWJS window and all of the test results would have to get logged to the output stream in order for them to be picked up by Testim and parsed out and displayed in the console. We also have to monitor whenever the test suite completes so that we can terminate the NWJS process. So there's also a server flag available that you can pass to this command. It's essentially the same thing. We're still configuring Testim to launch the NWJS process but we have to also open a WebSocket connection between that and Testim and that creates a two-way channel for communication between the two components. So NWJS can send all of the test results back to Testim to be displayed in this terminal window and Testim can also let NWJS know whenever files change and the tests need to rerun. So we're going to check that out in action. So this is still the markdown editor application that you saw earlier but I've got a bunch of tests written for them. So I'm just going to run NW test. So the application built and then... Okay, so that went really fast but the test did run in an NWJS window and then the window disappeared. That's because we knew the test terminated and we closed out the process. And then all the results are now displayed in the console. So let's do the same thing but now we're going to run it with the server flag. So the application is still going to build. All right, so now you can still see the tests running in this NWJS window however the window did not close out when the tests were done. So now you can interact with the tests you can rerun them and whatever and then over in the terminal you also have the results reported by Testum. This is still watching for file changes. So if I were to go into a test and change some stuff, make something fail. As soon as I save the application rebuilt and then now you have the results of your failed tests. So hopefully this will make things really easy moving forward to write integration tests for. Okay, so we saw development, we saw testing we're going to finish up with packaging. So with packaging you have to be able to identify the applications that the application files that you want to distribute in your final packaging and then zip them up into an archive file that has an NWU extension and then you want to merge that with the NWJS runtime and that's going to produce a single executable file that you can ship to your users. Now that looks really simple but the details do vary across different operating systems. So on Windows for example the final EXE that you get cannot function by itself. You also need to bundle some additional DLLs in order for things to work and then on a Mac the packaging steps are also different because the NWJS runtime is not a single binary it's a folder so now you have to place the application files inside the folder and distribute that. So those are all things that warrant some good automated script for but there's already this NPM package out there called Node Webkit Builder that does all the work for us and it's aware of all the details in the different operating systems. So we can use this. It's agnostic to build tools so you can write a wrapper for it and so we can simply extend MRCLI and the IDON to invoke that library and do all the work for us. And so now you can do that with this command which I'm going to demo and this is going to be the final demo of the presentation. Okay so this is still the markdown editor app however I have some desktop icons in here because you have to have desktop icons for your desktop apps and I also have a configuration file so this is why I specified that I want to build for these three platforms you don't have to specify any configuration file if you don't the IDON will just assume some defaults because they already understand your application structure so all we have to do now is run Ember NW package the first time that you run this the underlying Node Webkit module will download all the pre-built binaries for all the platforms that you specify you want to package for and so that way you don't have to re-download every time it's going to cache those binaries so in just a moment you're going to see something alright so packaging is done if I were to go up here now and then open up this build folder refresh you now have a folder for each of the platforms that we specified so in the windows location you've got the EXT file and all the dialogs that need to be shipped with you don't have to go and hand them down and this is the bundle for Mac so I'm going to open this up and we can check it out alright so I don't know if you guys can see this but it's the desktop application we have the pretty icon and all you have to do is ship this to your users double click and get the application that we saw earlier things are still functional you have the application menu you can still do things so it's still functional so that was packaging in just less than two minutes so we covered a bunch of things today however I do want to wrap up the talk quickly by going over how all of this can be used in the wild so we saw many examples earlier the editor, the popcorn time, the azure explorer desktop app it may be that some companies have a need for offline solutions so things that really don't need to be hosted anywhere in that dockyard we build a desktop app for a fitness club that specializes in indoor cycling they needed to have an application that could be installed on the desktop to help instructors teach a class so instructors could run a class or run a race for a particular duration and they could see exactly who was participating in the class and how they were doing all of the scores from riders were transmitted from bikes because we had special devices attached to the bikes and so all of the data were transmitted to the desktop app and then you could see as people were racing all of their scores show up in real time so that was one of the cooler projects that we were able to do at dockyard with this technology but you can also think of desktop applications that are powered with web technologies as a new way to distribute software so there are many enterprises out there that are very, they have very strict policies when it comes to upgrading systems to modern browsers but for whatever reason installing a desktop application is okay so we can, in the case of NWGS we're already dealing with a modern browser because it's Chromium under the hood, right? so that can definitely be an avenue to explore and even if you don't go on to build a desktop application I hope that you've gained something of value in this talk at least you were able to see how we were able to easily create custom solutions with Ember CLI through idons and also we are hiring at dockyard if you're interested in working on really cool Ember apps we are looking for senior Ember devs so if you're interested just come talk to me, come talk to Brian or any dockyard person that you see in the yellow shirts here and that's all, thank you for listening Questions? Kitabra has a question Have you had any experience or any attempts to package a Windows application all in one, like .msi or something where the DLLs are part of the executable all just as a single application? It doesn't work like that you basically, the best you can do is take the EXE the DLLs and zip them up Yeah, I was just thinking about like you were saying an enterprise client somebody who's running on an old Windows machine and not having to tell them to do this and this but you can ship them the zip file and all they have to do is unzip and then they have the EXE file they can create a desktop shortcut if they want and easily launch it from there Thanks Anybody else? What does the story around shipping an update to an existing app look like do you have to ship them like a new binary or can the existing app be aware that there's an update available? There are different ways of approaching that there's nothing that is really formalized but you can ship you can make sure that the binary is a very thin client essentially and maybe you can make sure that the app can sort of update itself so maybe whenever it knows that it's connected or maybe whenever the application launches it could detect to see if it's on the latest version and then just download assets down so that's one way of handling it Thanks Anybody else? Oh, not a question Would these apps be able to make it onto the Mac app store or the Windows app store? I do not believe so Why is that? I do not know I remember seeing some thread where people were asking the same question and there was something that prevented that from happening I'm not sure exactly it could have changed since I last checked it I think people are hungry and they want to listen to Keetar Bear Thank you very much Estelle