Cookbook

# Build a valid IPC message envelope
msg <- RDesk::rdesk_message("get_data", list(filter = "cyl == 6"))
str(msg)
#> List of 5
#>  $ id       : chr "msg_177463054444.706_580"
#>  $ type     : chr "get_data"
#>  $ version  : chr "1.0"
#>  $ payload  :List of 1
#>   ..$ filter: chr "cyl == 6"
#>  $ timestamp: num 1.77e+09

Copy-paste recipes for the most common RDesk patterns.

Load a CSV with a file dialog

app$on_message("load_file", function(payload) {
  path <- app$dialog_open(
    title   = "Open CSV",
    filters = list("CSV files" = "*.csv")
  )
  if (is.null(path)) return(invisible(NULL))

  df <- utils::read.csv(path, stringsAsFactors = FALSE)
  app$send("file_loaded", list(
    rows     = nrow(df),
    cols     = names(df),
    filename = basename(path)
  ))
})

Render a ggplot2 chart

RDesk Explorer Boxplot RDesk Models Page

app$on_message("get_chart", async(function(payload) {
  p <- ggplot2::ggplot(mtcars,
         ggplot2::aes(wt, mpg, colour = factor(cyl))) +
       ggplot2::geom_point(size = 3) +
       ggplot2::theme_minimal()

  list(chart = rdesk_plot_to_base64(p))
}, app = app))
rdesk.on("get_chart_result", function(data) {
  document.getElementById("chart").src =
    "data:image/png;base64," + data.chart;
});

Show a toast notification

app$toast("File saved successfully", type = "success")
app$toast("Could not connect",       type = "error")
app$toast("Update available",        type = "info")

Save a file with a dialog

app$on_message("export_csv", function(payload) {
  path <- app$dialog_save(
    title        = "Save CSV",
    filters      = list("CSV files" = "*.csv"),
    default_name = "export.csv"
  )
  if (is.null(path)) return(invisible(NULL))

  write.csv(mtcars, path, row.names = FALSE)
  app$toast(paste("Saved to", basename(path)), type = "success")
})

Run a slow computation without freezing the UI

app$on_message("run_analysis", async(function(payload) {
  result <- slow_function(payload$data)
  list(
    summary = as.list(summary(result)),
    n       = length(result)
  )
}, app = app, loading_message = "Analysing..."))

Add a native menu

app$on_ready(function() {
  app$set_menu(list(
    File = list(
      "Open..."   = function() app$send("load_file", list()),
      "Export..." = function() app$send("export_csv", list()),
      "---",
      "Exit"      = app$quit
    ),
    View = list(
      "Refresh"   = function() app$send("refresh", list()),
      "Dark mode" = function() app$send("toggle_theme", list())
    ),
    Help = list(
      "About"     = function() {
        app$toast("MyApp v1.0.0 -- built with RDesk", type = "info")
      }
    )
  ))
})

Check for and install updates

# In app.R, before app$run()
rdesk_auto_update(
  current_version = "1.0.0",
  version_url     = "https://yourserver.com/latest.txt",
  download_url    = "https://yourserver.com/MyApp-setup.exe",
  app             = app
)

Host a plain text file at version_url containing only the latest version string, e.g. 1.1.0. RDesk checks it silently on launch and installs the update if a newer version is available.

Send data when the app starts

app$on_ready(function() {
  df <- load_initial_data()
  app$send("data_ready", list(
    rows = lapply(seq_len(nrow(df)), function(i) as.list(df[i,])),
    cols = names(df),
    n    = nrow(df)
  ))
})

Handle errors gracefully

app$on_message("risky_operation", async(function(payload) {
  tryCatch({
    result <- operation_that_might_fail(payload)
    list(success = TRUE, data = result)
  }, error = function(e) {
    list(success = FALSE, error = e$message)
  })
}, app = app))
rdesk.on("risky_operation_result", function(data) {
  if (!data.success) {
    showError(data.error);
    return;
  }
  renderResult(data.data);
});

Detect if running as a built app vs dev mode

if (rdesk_is_bundle()) {
  # Running as distributed exe
  data_path <- file.path(getwd(), "data", "config.json")
} else {
  # Running in development via source("app.R")
  data_path <- file.path(app_dir, "data", "config.json")
}