I'm going to be giving a talk this Thursday at my local R/Data Science Meetupabout my method for deploying self contained desktop R applications. Since my original post on the subject (over 2 years ago!) I've made manyof improvements thanks to the many useful comments I received and my own “dog-fooding”.
So many in fact that the framework is a project in its own right, which I'm calling DesktopDeployR. This post will officially document the changes I've made and share the project to the greater R/Data Science community.
If you haven't already, I recommend reading my original post to understand the fundamentals of how such deployments are done.
For the impatient, the TL;DR summary is: on a Windows system, use R-Portable and Windows Scripting Host to launch a Shiny app in a user's default web browser.
System and R scripts for app launch are more separated from app specific code. Specifically, the framework's directory structure is now:
<app>/ +- app/ | +- library/ # <- private app library | +- shiny/ # <- where shiny app files go | +- app.R | +- config.cfg | +- packages.txt +- dist/ # <- the core framework | +- R-Portable/ | +- script/ +- log/ +- appname.bat
This means you can drop a pre-made Shiny application alongside the launch framework and it should work with minimal effort.
Being app-agnostic also means that the the framework is not specific to Shiny apps. I have successfully tested it with RGtk and Tcl/Tk based apps as well. It is just a matter of putting the necessary code to start your app in
app.R. For a Shiny app, this is simply the following line:
App launch is configurable via a JSON config file (shown above in the
app/folder). There are options to configure:
- the path to an R installation, so that a system one can be specified instead of R-Portable, making the deployed app size smaller (if that's important to you).
- the CRAN mirror to use for installing package dependencies
- where error logs are stored - e.g. with the app in the
log/folder or in a user's home directory on the system.
Primary app package dependencies are specified in a
packages.txtfile which is used to create a private package library when the app is first launched. This was inspired by
requirements.txtfiles used to install a set of Python packages using
The private library is added to
.libPaths()at launch, so modifying an
Rprofile.sitefile is no longer necessary.
Primary app package dependencies are also "ensured", meaning if they change (i.e. new ones are added) they are installed into the private library the next time the app is launched.
There is a GUI progress bar displayed during launch that updates as primary package dependencies are loaded. This is useful feedback that the app is actually doing something, especially if there a many dependencies to load.
As before, you still need to download the version of R-Portableyou need and install it into a template framework that you clone for each deployment. However, Since the app uses a private library for dependencies, the R-Portable install can stay and be launched "vanilla", which makes swapping it out (e.g. for upgrades) much easier.
Chrome Portable is no longer part of the framework. It behaved very inconsistently and would generate errors that I hadn't a clue how to debug. The current crop of browsers (IE10+ included) all work well with Shiny. This is also a moot point if you're deploying a non-Shiny app.
Now that the framework is more portable, I can also more easily open source it. If you want to give it a try with your own projects the GitHub repository is here. I'd also appreciate any feedback (or help) to make it better.