running a shiny.appobj and setting a resource (e.g. www) path

During development, I often prefer to create an explicit shiny.appobj object via shinyApp(). For example, rather than:

runApp(".")  ## app directory-based execution

... I'll instead prefer:

app <- shinyApp(ui, server)  ## ui and server are defined in my workspace
runApp(app)  ## app object-based execution

For additional app resources however, the latter approach doesn't seem to auto-map the www subdirectory of my current working directory to the . resource path in the app. So when I have a file ./www/foo.jpg and include in my UI something like:

ui <- fluidPage(tags$img(src = "foo.jpg"))
## runApp(".")  <-- foo.jpg resolves
## runApp(app)  <-- foo.jpg 404s
  • foo.jpg resolves when I runApp("."), i.e. run from the current working directory.
  • foo.jpg 404s when I runApp(app), i.e. the www resource mapping didn't happen this time.

Does anyone know of the proper way to runApp with a shiny.appobj argument while also gaining (or specifying) a relative directory for resource path mappings?

(I should note why I prefer this pattern during development: the app has a startup preamble that loads a large dataset ... even when serialized as an RDS structure this takes an annoyingly long time to reload on each app iteration during development work.)

What follows has all the hallmarks of a hack, this should probably be escalated to the github issues for a 'proper solution' but in the short term this might work for you.
After you manually create the app, you manually attach the component that runApp will look for the www resource path


app <- shinyApp(ui = ui, server = server)

app$staticPaths <- list(`/` = structure(list(
  path = file.path(getwd(), "www"),
  options = structure(list(
    indexhtml = FALSE, fallthrough = TRUE,
    html_charset = NULL, headers = NULL, validation = NULL,
    exclude = FALSE
  ), class = "staticPathOptions", normalized = TRUE)
), class = "staticPath"))

runApp(app)
#or maybe even just 
app

You can use addResourcePath to make static resources available to Shiny's web server. Using the app directory-based execution this is done automatically for the www folder:

library(shiny)

if(!dir.exists("www")){
  dir.create("www")
}

addResourcePath(prefix = "www", directoryPath = "./www")

# create test image -------------------------------------------------------
png(filename = file.path("./www/test.png"))
x <- 1:50
y <- runif(50)
plot(x, y, type = "l")
invisible(dev.off())

ui <- fluidPage(
  tags$img(src = "www/test.png")
)

server <- function(input, output, session) {}

app <- shinyApp(ui, server)

runApp(app)