1

I have the following Shiny package:

DESCRIPTION:

Package: mypackage
Version: 0.0.1
Depends: shiny

R/mypackage.R:

ui <- shiny::fluidPage(getwd())
server <- function(input, output, session) {}

To install and run, I do

R -q -e "devtools::install(); shiny::runApp(shinyApp(mypackage:::ui, mypackage:::server))"

And when I do this, my app outputs

/tmp/RtmpC1viCa/R.INSTALL6931e8be933/mypackage

which does not exist.

Why is that, and how I improve that? I have seen that getwd() may not actually return the users working directory - but why is it returning a non-existent one?

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
bers
  • 4,817
  • 2
  • 40
  • 59
  • To see the problem, it helps to just paste `shiny::fluidPage(getwd())` into the R console: that gives `
    C:/Users/bers
    `
    – bers Sep 23 '21 at 07:14

1 Answers1

3

Inside a Shiny app, getwd() refers to the application directory.

However, since you are creating a package, you need to be aware of, and careful about, where/when code is executed.

Code at file scope inside a package is executed at installation time. And installation of R packages happens inside a temporary directory to isolate it. In fact, R CMD check will warn you about this.

If you need the value of getwd() (as well as other path specific functions such as system.file), you mustn’t call it at file scope.

The solution is to use the .onLoad package hook. In your case, you need to create and assign the entire UI at package load time:

.onLoad <- function (libname, pkgname) {
    assign(
        'ui',
        shiny::fluidPage(getwd()),
        topenv()
    )

}

If you dislike using assign, $<- also works:

.onLoad <- function (libname, pkgname) {
    topenv()$ui <- shiny::fluidPage(getwd()),
}

But alternatively you could also create a function that returns the UI:

ui <- function () {
    shiny::fluidPage(getwd())
}

And run it via

shiny::runApp(shinyApp(mypackage:::ui(), mypackage:::server))
Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • That makes a lot of sense, thank you for the explanation! Of course, my real app has a much greater depth of functions calling one another. It's amazing that all this (UI) code was run only once during installation, and still worked perfectly until I came across the issue with the working directory. – bers Sep 22 '21 at 10:39
  • As you explained, I cannot access the "real" working directory anyway. Assuming that is true, what if I simply left everything as is - are there any downsides to be expected? Is the run-code-at-install-time variant maybe even faster? – bers Sep 22 '21 at 10:40
  • 1
    @bers Sure, running code at installation will potentially make package loading faster. But be sure to benchmark this: it’s quite likely not a substantial improvement. (Aside: the fact that Shiny changes the user’s working directory is extremely annoying, and arguably a bug. If you want to work around this, you’ll need to store the value of `getwd()` *before* creating the Shiny application, i.e. before calling `shiny::runApp`.) – Konrad Rudolph Sep 22 '21 at 11:07
  • I think the biggest argument against running code at installation is that future versions of dependencies will not be taken into account, until `mypackage` itself is reinstalled. I will go for `ui()` now - thanks :) – bers Sep 23 '21 at 07:12