%%{init: {'theme': 'neutral', 'themeVariables': { 'fontFamily': 'monospace', "fontSize":"16px"}}}%%
flowchart TD
subgraph Dir["<strong>App-Package Directory</strong>"]
Init["<code>renv::init()</code>"]
Snap["<code>renv::snapshot()</code>"]
end
subgraph RepEnv["<strong>Reproducible Environment</strong>"]
Lock["<code>renv.lock</code> file"]
Lib["<code>renv/</code> library"]
end
Init --> Snap
Snap -->|"<em>Captures dependencies<br>in lockfile</em>"| Lock
Snap -->|"<em>Stores packages<br>in private library</em>"| Lib
style Dir stroke:1px,rx:3,ry:3,font-size:13px
style RepEnv stroke:1px,rx:3,ry:3,font-size:13px
6 Dependencies
Dependencies are the must-have components for your app-package, and they can be divided into imports and exports.
Imports are the functions we’re borrowing from add-on/third party packages (i.e., any packages not automatically loaded in a new R session).
Exports are the functions, data, and other R objects our app-package offers to users.
In this chapter we’ll cover how to manage dependencies in your new app-package.
Dependencies are handled with the NAMESPACE (generated via roxygen2 tags) and in the DESCRIPTION file. Together, these files determine which packages and functions our app-package depends on, and the functions and objects we’re making available to anyone using our app-package.
This chapter will pick up where we left off with the 05_roxygen2 branch of sap. Below is a folder tree of it’s contents:
sap/
├── DESCRIPTION
├── NAMESPACE
├── R/
│ ├── mod_scatter_display.R
│ ├── mod_var_input.R
│ ├── launch_app.R
│ ├── movies_server.R
│ ├── movies_ui.R
│ └── utils.R
├── README.md
├── app.R
├── man/
│ ├── mod_scatter_display_server.Rd
│ ├── mod_scatter_display_ui.Rd
│ ├── mod_var_input_server.Rd
│ ├── mod_var_input_ui.Rd
│ ├── launch_app.Rd
│ ├── movies_server.Rd
│ ├── movies_ui.Rd
│ └── scatter_plot.Rd
├── movies.RData
├── sap.Rproj
└── www/
└── shiny.png
4 directories, 21 files- 1
-
The
manfolder now contains the help (.Rd) files for the functions inR/
When in doubt…load, document, and install
During development, you might lose track of the last devtools function you called (I know I do). If this happens, I’ve found loading, documenting, and installing helps to re-orient me to the current state of the package.
Ctrl/Cmd + Shift + L
Ctrl/Cmd + Shift + D
Ctrl/Cmd + Shift + B
It’s also satisfying to see all three functions execute without any errors!
Managing dependencies
The first step in managing dependencies is identifying which packages sap relies on. In the Shiny chapter I introduced the renv package (see Section 2.6.2), which is designed for project-level dependency management. renv ensures the same package versions are used across different systems by capturing R and package versions in a lockfile (renv.lock) and a private library (renv/library).1
R packages are designed to distribute and reuse code, so the dependencies are listed in the DESCRIPTION file under Imports, Depends, Suggests, and Remotes. The packages listed in the DESCRIPTION of our app-package will be different than those listed in a renv.lock file because not every package used to create an app-package needs to be used to use and run said app-package.2 However, it’s worth noting that installing an R package doesn’t enforce the strict version control like renv.
The diagram below outlines the basic process for importing functions from add-on packages to use in our locally developed package, and then exporting those functions for people to use when they install/load our package.3
%%{init: {'theme': 'neutral', 'themeVariables': { 'fontFamily': 'monospace', "fontSize":"15px"}}}%%
flowchart TD
subgraph RPkg["<strong>R Package (<code>pkg</code>)</strong>"]
fun("Exports <code>fun()</code> function")
end
subgraph SapPkg["<strong><code>sap</code></strong>"]
sap("Uses <code>pkg::fun()</code> for local<br><code>foo()</code> function")
end
Users("Users install/load <code>sap</code> to use <code>foo()</code>")
RPkg -->|"<em>imports <code>fun()</code> from <code>pkg</code></em>"| SapPkg
SapPkg -->|"<em>exports <code>foo()</code> from <code>sap</code></em>"| Users
style RPkg stroke:1px,rx:3,ry:3,font-size:15px
style SapPkg stroke:1px,rx:3,ry:3,font-size:16px
Our goal is to limit the dependencies to only those critical to the functioning of our app, because each additional dependency is a potential point of failure (should this package become unavailable or significantly change).
A great place to start is our app.R file:
# pkgs <- c("shiny", "shinythemes", "stringr", "ggplot2", "rlang")
# install.packages(pkgs, quiet = TRUE)
# packages ------------------------------------
library(shiny)
library(shinythemes)
library(stringr)
library(ggplot2)
library(rlang)
# launch_app ------------------------------------
launch_app()Ideally, we’ll want to replace these calls to libary(), but first we have to make sure the functions we’re using in these packages will be available in sap.
When we run the contents of app.R, we see the following:
> launch_app()
Error in launch_app() : could not find function "launch_app"Why can’t R find the "launch_app" function in app.R?
Let’s recap what we’ve done so far:
app.R
- The
app.Rfile loads the necessary packages and callslaunch_app():
sap/
└── app.RR/
- The
R/launch_app.Rfile contains the code androxygen2documentation forlaunch_app()function:
sap/
└── R/
└── launch_app.Rman/
roxygen2generates theman/launch_app.Rddocumentation file:
sap/
└── man/
└── launch_app.RdThe error above is telling us that despite having documentation for launch_app() in the R/ folder and generating the corresponding .Rd file in man/, launch_app() isn’t being exported from sap.
6.1 Package exports
The exact cause of the error above becomes more apparent when we try to explicitly namespace launch_app() from sap:4
sap::launch_app()Error: 'launch_app' is not an exported object from 'namespace:sap'Launch app with the shinypak package:
launch('06.1_pkg-exports')To make the launch_app() function available to users of our package, we need to export it by including the @export tag in the roxygen2 comment block:
@export: make function available to users of sap.#' @export my_func #' my_func <- function() { #' #' }- 1
- Placed above the function we want to export (function name is not required)
Export launch_app()
We’ll export launch_app() from sap by placing the @export tag above the function in R/launch_app.R:
#' Launch the Movies Review Application
#'
#' Starts the Movies Review Shiny application, which provides a customizable
#' scatter plot interface for analyzing movie data.
#'
#' @return A **Shiny application** object.
#'
#' @section Details:
#' The application uses:
#' - **UI**: Defined in [`movies_ui()`].
#' - **Server Logic**: Defined in [`movies_server()`].
#'
#' @seealso
#' - [`movies_ui()`] for the user interface.
#' - [`movies_server()`] for the server logic.
#'
#' @family **Standalone Application**
#'
#' @examples
#' if (interactive()) {
#' launch_app()
#' }
#'
#' @export
launch_app <- function() {
shiny::shinyApp(ui = movies_ui, server = movies_server)
}In app.R, we’ll replace the calls to library() with a single call to library(sap)
# packages ------------------------------------
library(sap)
# launch_app ------------------------------------
launch_app()We’ll document the package to generate the NAMESPACE changes:
Ctrl/Cmd + Shift + D
Now, when we run the code app.R, we see the following:
We’ve lost the Shiny icon (www/shiny.png) in the UI, but we’ll address this in Chapter 9.
launch_app() launches our application!
The NAMESPACE file now contains a single export (launch_app), and when we enter sap:: in the Console, we see the launch_app() function help file in the tab completion.
What @export does
We’ll pause here to notice a few things about what @export does. When we documented our package, the code was automatically loaded before the NAMESPACE was updated with export(launch_app).
> devtools::document()
ℹ Updating sap documentation
ℹ Loading sap
Writing NAMESPACE- 1
-
Call to
devtools::load_all()
document() will call load_all() to make sure all the changes in the R/ folder are included in the updated documentation.
%%{init: {'theme': 'neutral', 'themeVariables': { 'fontFamily': 'monospace', "fontSize":"13px"}}}%%
flowchart
subgraph R["<strong>R/ folder</strong>"]
Tag("Add <code>@export</code><br>to function<br>documentation")
end
subgraph NS["<strong>NAMESPACE</strong>"]
Exported(["<code>export(launch_app)</code>"])
end
subgraph Man["<strong>man/ folder</strong>"]
RdFile(["<code>.Rd</code> files created"])
end
Document[["Run <code>document()</code>"]]
Load("Calls <code>load_all()</code>")
Tag --> Document
Document -.-> Load --> NS & Man
We can confirm launch_app() has been exported with ls(), which returns “the names of the objects in the specified environment.
ls(name = "package:sap")[1] "launch_app"The search() list
library(sap) attaches sap to the search list. We can view all the attached packages in the string returned from search():
"package:sap" %in% search()[1] TRUEWhat about the add-on/third-party package functions launch_app() relies on, like ggplot2? Let’s check to see if ggplot2 is also attached to the search() list:
c("package:ggplot2") %in% search()[1] FALSEWhy does this matter? Because if these packages aren’t attached to the search() list, we can’t call their functions directly (the way we would if we’d loaded the package with library()). For example, if we try to use ggplot2 to build a plot (similar to the one we have in the app), we see the following:
ggplot(data = mtcars,
aes(x = disp, y = mpg)) +
geom_point()
# Error in ggplot(data = mtcars, aes(x = disp, y = mpg)) :
# could not find function "ggplot"We can use the add-on/third-party package functions sap relies on, but we need to explicitly namespace these functions from their original package namespaces (i.e., using pkg::fun()):
We can use ggplot2 if we explicitly namespace it’s functions
Access to add-on/third-party package functions has implications for the other functions in sap–for example, the scatter_plot() function uses ggplot2 functions. But we’re not exporting scatter_plot(), so when we attempt to run the examples, we see the following error:
scatter_plot() function without exporting
Examples for ‘sap::scatter_plot’
The message at the top of the Help pane is informative because it tells us that despite scatter_plot() being functional when we run launch_app(), it’s not part of the package namespace (and thus, not accessible to users in the help file).
Export scatter_plot()
Let’s add the @export tag to R/scatter_plot.R so it’s exported from sap.
#' Create scatter plot
#'
#' Custom [`ggplot2`](https://ggplot2.tidyverse.org/) function for building
#' scatter plots in `sap`.
#'
#'
#' @param df `data.frame` or `tibble`
#' @param x_var string variable mapped to `x` axis
#' @param y_var string variable mapped to `y` axis
#' Generate a Scatter Plot
#'
#' Creates a scatter plot using `ggplot2` with the specified data and
#' aesthetics.
#'
#' @param df *(data.frame)* The dataset containing the variables to plot.
#' @param x_var *(character)* Name of the variable for the x-axis.
#' @param y_var *(character)* Name of the variable for the y-axis.
#' @param col_var *(character)* Name of the variable for the color aesthetic.
#' @param alpha_var *(numeric)* Transparency level of points (0 to 1).
#' @param size_var *(numeric)* Size of points.
#'
#' @return A `ggplot` object representing the scatter plot.
#'
#' @section Details:
#' `scatter_plot()` is designed for use in Shiny applications but can also be
#' used independently.
#' It supports customization of transparency, size, and color aesthetics.
#'
#' @seealso
#' - [`mod_scatter_display_server()`] for integrating this function into the
#' scatter plot module.
#' - [`ggplot2::ggplot()`](https://ggplot2.tidyverse.org/) for details on
#' `ggplot2` usage.
#'
#' @family **Utility Functions**
#'
#' @examples
#' scatter_plot(
#' df = mtcars,
#' x_var = "mpg",
#' y_var = "hp",
#' col_var = "cyl",
#' alpha_var = 0.7,
#' size_var = 3
#' )
#'
#' @exportAfter documenting sap, the NAMESPACE is updated with the export() directive:
Ctrl/Cmd + Shift + D
The contents of the updated NAMESPACE file and typing sap:: in the Console now displays the scatter_plot() help file in the tab completion:
Below, we confirm users can access the help file for scatter_plot() and run the examples:
loadedNamespaces()
We’ve already confirmed that ggplot2 isn’t attached with sap (and hence, it is not included in the search() list)
c("package:ggplot2") %in% search()[1] FALSEHowever, we can access the functions we used the pkg::fun() syntax with because those functions are included in the loaded namespaces (which we can view with loadedNamespaces())
c("ggplot2") %in% loadedNamespaces()[1] TRUEWhat to @export
‘Always err on the side of caution, and simplicity. It’s easier to give people more functionality than it is to take away stuff they’re used to’ - What to export, R Packages, 2ed
When determining which functions to export, consider the question: “When a user installs and loads sap, what functions do I want to be available?”
In app-packages, I’ll take the following general approach:
Start by exporting the standalone app function (
launch_app())Then selectively export modules and/or functions that perform distinct tasks with potentially reusable functionality (i.e., generate specific UI components, perform data processing tasks, etc.).
It’s rare that I don’t export functions from app-packages, but I like to make sure users have the ability to get ‘under to hood’ and see how each part of an application works.
If you’d like to the Low-key @exports with @keywords internal box below for exporting functions without including them in your package index.
6.2 Package imports
Launch app with the shinypak package:
launch('06.2_imports')Importing dependencies is slightly more involved than exports because imports are managed by both the DESCRIPTION and the NAMESPACE:
The
DESCRIPTIONfile handles package-level dependencies, specifying which add-on packages our app-package uses.The
NAMESPACEmanages function-level access, importing functions from add-on packages to be used in our app-package, and–as we’ve seen above–exporting functions from our app-package for others to use.
Package-level depencencies
The DESCRIPTION file manages dependencies with three fields: Depends, Imports, and Suggests. Most add-on packages belong under the Imports field (i.e., functions from these packages are used in the code below R/).5
Depends
Packages listed under Depends are essential for our app-package to work. These packages will be attached before our package when library(sap) is called.
Imports
Packages listed under Imports are necessary for our app-package to work. These packages are loaded (but not attached) when our app-package is installed.
Suggests
The Suggests field should include any packages that enhance our app-package, but aren’t necessary for the basic functionality. This might include packages used in examples, vignettes, tests, etc.
Function-level access
Function-level access is managed using namespace-qualified references (or ‘explicit namespacing’) in the code below R/. The NAMESPACE can also be used to include add-on packages or functions with the @import and @importFrom tags.6
Namespace-qualified referencing: Refer to add-on package functions using
pkg::fun()syntax in the code belowR/.Special imports:
@importFromshould be used when 1) “You can’t call an operator from another package via::” 2) “importing a function makes your code much more readable” (not easier to write)Importing everything:
@importshould be used if “you make such heavy use of so many functions from another package that you want to import its entire namespace”
Handling imports
The workflow I use to manage add-on dependencies comes from the advice in the roxygen2 documentation:
“if you are using just a few functions from another package, we recommending adding the package to the
Imports:field of theDESCRIPTIONfile and calling the functions explicitly using::, e.g.,pkg::fun()…”…“If the repetition of the package name becomes annoying you can
@importFromand drop thepkg::fun()”. - Importing functions
Include the add-on package to the
Importsfield withusethis::use_package().Refer to add-on functions using explicit namespacing (i.e.,
pkg::fun()) in the code beneathR/.
We have some special considerations for the imported add-on functions in our app-package:
Using @import
A substantial portion of the code in sap comes from shiny, so we’ll remove the explicit namespacing and place the @import tag in R/launch_app.R7
show/hide R/launch_app.R
#' Launch the Movies Review Application
#'
#' Starts the Movies Review Shiny application, which provides a customizable
#' scatter plot interface for analyzing movie data.
#'
#' @return A **Shiny application** object.
#'
#' @section Details:
#' The application uses:
#' - **UI**: Defined in [`movies_ui()`].
#' - **Server Logic**: Defined in [`movies_server()`].
#'
#' @seealso
#' - [`movies_ui()`] for the user interface.
#' - [`movies_server()`] for the server logic.
#'
#' @family **Standalone Application**
#'
#' @examples
#' if (interactive()) {
#' launch_app()
#' }
#'
#' @import shiny
#' - 1
- Import entire shiny package namespace
Using @importFrom
.data can’t be exported using ::, so we’ll include @importFrom in R/scatter_plot.R. On the other hand, ggplot2 has over 400 functions, so we’ll add the package to the Imports field and use the namespace-qualified references.8
show/hide R/scatter_plot.R
#' Create scatter plot
#'
#' Custom [`ggplot2`](https://ggplot2.tidyverse.org/) function for building
#' scatter plots in `sap`.
#'
#'
#' @param df `data.frame` or `tibble`
#' @param x_var string variable mapped to `x` axis
#' @param y_var string variable mapped to `y` axis
#' Generate a Scatter Plot
#'
#' Creates a scatter plot using `ggplot2` with the specified data and
#' aesthetics.
#'
#' @param df *(data.frame)* The dataset containing the variables to plot.
#' @param x_var *(character)* Name of the variable for the x-axis.
#' @param y_var *(character)* Name of the variable for the y-axis.
#' @param col_var *(character)* Name of the variable for the color aesthetic.
#' @param alpha_var *(numeric)* Transparency level of points (0 to 1).
#' @param size_var *(numeric)* Size of points.
#'
#' @return A `ggplot` object representing the scatter plot.
#'
#' @section Details:
#' `scatter_plot()` is designed for use in Shiny applications but can also be
#' used independently.
#' It supports customization of transparency, size, and color aesthetics.
#'
#' @seealso
#' - [`mod_scatter_display_server()`] for integrating this function into the
#' scatter plot module.
#' - [`ggplot2::ggplot()`](https://ggplot2.tidyverse.org/) for details on
#' `ggplot2` usage.
#'
#' @family **Utility Functions**
#'
#' @examples
#' scatter_plot(
#' df = mtcars,
#' x_var = "mpg",
#' y_var = "hp",
#' col_var = "cyl",
#' alpha_var = 0.7,
#' size_var = 3
#' )
#'
#' @export
#'
#' @importFrom rlang .data
#' - 1
-
Import a the
.dataoperator fromrlang
use_package('pkg')
As an example, we’ll add the bslib package and update our app UI layout:
usethis::use_package('bslib')✔ Setting active project to '/Users/mjfrigaard/projects/apps/sap'
✔ Adding 'bslib' to Imports field in DESCRIPTION
• Refer to functions with `bslib::fun()`In movies_ui(), we’ll change the fluidPage() to the bslib::page_fillable() and adjust move the data source information to the bslib::card_footer():
updated movies_ui() bslib function
#' User Interface for the Movies Review Application
#'
#' Creates the user interface (UI) for the Movies Review application, which
#' allows users to create customizable scatter plots based on movie data.
#'
#' @return A Shiny `tagList` object containing the UI elements.
#'
#' @section Details:
#' The interface is built using [`bslib`](https://rstudio.github.io/bslib/)
#' - **Page (fillable)**: [`bslib::page_fillable()`](https://rstudio.github.io/bslib/reference/page_fillable.html)
#' displays the app title.
#' - **Sidebar**: [`bslib::layout_sidebar()`](https://rstudio.github.io/bslib/reference/sidebar.html)
#' includes a logo and the variable
#' selection module.
#' ([`mod_var_input_ui`]).
#' - **Card**: [`bslib::card()`](https://rstudio.github.io/bslib/reference/card.html)
#' displays the scatter plot module
#' ([`mod_scatter_display_ui`]).
#'
#' @seealso
#' - [`movies_server()`] for the server logic of the app.
#' - [`mod_var_input_ui()`] and [`mod_scatter_display_ui()`] for the modules
#' included in the UI.
#'
#' @family **Application Components**
#'
#' @examples
#' if (interactive()) {
#' shiny::shinyApp(ui = movies_ui(), server = movies_server)
#' }
#'
movies_ui <- function() {
tagList(
bslib::page_fillable(
h1("Movie Reviews"),
bslib::layout_sidebar(
sidebar =
bslib::sidebar(
title = tags$h4("Sidebar inputs"),
img(
src = "shiny.png",
height = 60,
width = 55,
style = "margin:10px 10px"
),
mod_var_input_ui("vars")
),
bslib::card(
full_screen = TRUE,
bslib::card_header(
tags$h4("Scatter Plot")
),
mod_scatter_display_ui("plot"),
bslib::card_footer(
tags$blockquote(
tags$em(
tags$p(
"The data for this application comes from the ",
tags$a("Building web applications with Shiny",
href = "https://rstudio-education.github.io/shiny-course/"
),
"tutorial"
)
)
)
)
)
)
)
)
}After adding the add-on packages to the DESCRIPTION with usethis::use_package(), then deciding if/where to use @importFrom and @import, we’ll load, document, and install sap:
Ctrl/Cmd + Shift + L
Ctrl/Cmd + Shift + D
Ctrl/Cmd + Shift + B
When we review the updated NAMESPACE and DESCRIPTION files, we should see the following:
roxygen2 will update the NAMESPACE, but usethis::use_package() is needed to update the DESCRIPTION.
When we run launch_app(), we see the application launches and we can still run the scatter_plot() examples:
What @import does
The figure below attempts to capture some of confusion between the dependencies listed in the NAMESPACE and the Imports field in the DESCRIPTION file.9
%%{init: {'theme': 'neutral', 'themeVariables': { 'fontFamily': 'monospace', "fontSize":"13px"}}}%%
flowchart TD
subgraph DESCRIPTION["<strong>DESCRIPTION</strong>"]
UsePkg["<code>use_package('pkg')</code>"]
Imports("<code>Imports</code><br> in DESCRIPTION")
end
subgraph R["<strong>R/ Folder</strong>"]
Tag("Use <code>pkg::fun()</code><br>")
Import("Use <code>@import</code>")
ImportFrom("Use <code>@importFrom</code>")
end
subgraph NAMESPACE["<strong>NAMESPACE</strong>"]
NSImport("<code>import(pkg)</code>")
NSImportFrom("<code>importFrom(fun,pkg)</code>")
end
Document(["<code>devtools::document()</code></strong>"])
UsePkg -->|"Adds <code>pkg</code> to"|Imports
DESCRIPTION ==>|"To use <code>fun()</code><br>from <code>pkg</code>..."|Tag
DESCRIPTION -.-> |"To use everything<br>from <code>pkg</code>..."|Import
DESCRIPTION -.-> |"For special cases<br>from <code>pkg</code>..."|ImportFrom
Tag ==> Document
Import -.-> Document -.-> |"Adds <code>pkg</code> to"|NSImport
ImportFrom -.-> Document -.-> |"Adds <code>fun, pkg</code> to"|NSImportFrom
devtools::document does not change the DESCRIPTION filedevtools::document() (or Ctrl/Cmd + Shift + D) updates the NAMESPACE with any @import, @importFrom or @export tags. However, no changes are made to the DESCRIPTION file.
Let’s confirm we’re still only exporting launch_app() and scatter_plot() from sap:
ls(name = "package:sap")[1] "launch_app" "scatter_plot"Great. Now we’ve listed six packages in the Imports field of the DESCRIPTION file:
Imports:
bslib,
ggplot2,
rlang,
shiny,
stringr,
toolsThe search() list
Are these packages on the search list?
pkgs <- c("package:bslib", "package:ggplot2",
"package:rlang", "package:shiny",
"package:stringr", "package:tools")
pkgs %in% search()[1] FALSE FALSE FALSE FALSE FALSE FALSEThis demonstrates that none of these packages are attached with sap.
loadedNamespaces()
However, the rlang and shiny packages are included in the loadedNamespaces() (because we included them with @import/@importFrom).
pkgs <- c("bslib", "ggplot2", "rlang",
"shiny", "stringr", "tools")
pkgs %in% loadedNamespaces()[1] FALSE FALSE TRUE TRUE FALSE TRUEWe can still access the add-on package functions in sap using the pkg::fun() syntax:
Imports FAQ
Below are handful of questions and answers I’ve encountered regarding package imports:
Question 1: How can I include an add-on package to my DESCRIPTION file?
Answer 1: usethis::use_package() automatically adds a package in the Imports section, and has options for specifying the minimum version.
Question 2: Will users of my app-package have access to the packages listed in the Imports field of my DESCRIPTION file?
Answer 3: library(pkg) loads the namespace of imported packages, but they are not attached to the search() path.10
Question 4: How can I tell the difference between functions written by a package author and imported functions in the code below R/?
Answer 4: using pkg::fun() makes calls to add-on packages explicit and easy to differentiate from the native functions developed in sap.11
Question 5: What does the NAMESPACE do when my package is installed by a user?
Answer 5: managing the NAMESPACE ensures your app-package works when it’s installed and loaded on another machine, because R will read your package namespace to find what it imports and exports.12
Question 6: Where should I place the @importFrom tag in the code below R/?
Answer 6: place the @importFrom pkg fun tag directly above the code using the add-on function. You can also consolidate all @import and @importFrom tags into a single package doc file (i.e., R/[sap]-package.R) by calling usethis::use_package_doc().
Question 7: Should I be using @importFrom or @import from?
Answer 7: prefer @importFrom over @import, but try to avoid using either.13 14
Question 8: Where can I find more information about package namespaces and imports?
Answer 8a: Imports are described briefly in R Packages, 2ed15 and covered in-depth in Advanced R, 2ed.16
Answer 8b: “Each namespace has an imports environment that can contain bindings to functions used by the package that are defined in another package.”
Answer 8c: “The imports environment is controlled by the package developer with the NAMESPACE file. Specifically, directives such as importFrom() and imports() populate this environment.”
In order for app-package to work, users needs to have access to any add-on packages that are called in the code below R/. Knowing when, why, how and what happens to imports helps you decide how to fit these habits into your package development workflow.
Checking dependencies
With all the moving parts in dependency management, it can be easy to forget if you’ve documented everything correctly. So far we haven’t covered using devtools::check() as part of your app-package habits (which is fine), but this is one area it’s particularly helpful.
For example, if I had listed shiny as an import using the @import tag (resulting in the import(shiny) directive in the NAMESPACE), devtools::check() would produce the following error:
── R CMD check results ────────────────────────── sap 0.0.0.9620 ────
Duration: 7.4s
❯ checking package dependencies ... ERROR
Namespace dependency missing from DESCRIPTION Imports/Depends entries: ‘shiny’
See section ‘The DESCRIPTION file’ in the ‘Writing R Extensions’
manual.
1 error ✖ | 0 warnings ✔ | 0 notes ✔
Error: R CMD check found ERRORs
Execution halted
Exited with status 1.Recap
Below are the main takeaways from managing the imports and exports from your app-package:
If you’d like to read more about package dependencies, I recommend Writing R Extensions (specifically the sections on dependencies 17 and namespaces 18).
In the next section, we’ll cover how to ensure the movies.RData can be stored and loaded in our app-package!
Although the branches throughout the book do not use
renv, I highly recommend adopting this practice (especially if you’re collaborating with other developers).↩︎We’re using
devtoolsandusethisto develop our app-package, but these packages are not needed for our code to run.↩︎I’ve made this process somewhat easier by explicitly namespacing all of the add-on package functions in
sap(i.e., withpkg::fun()).↩︎Read more in the Exports section of R Packages, 2nd Ed↩︎
Additional fields exists (i.e.,
Remotes), but these are special circumstances.↩︎Read more about this in the section titled, ‘In code below R/’ in R Packages, 2ed↩︎
Using
@importis not generally considered best practice, but it makes sense for app-packages: …for Shiny apps, I recommend using@import shinyto make all the functions in the Shiny package easily available. Mastering Shiny, R CMD check”↩︎Read more about using
ggplot2in packages in the section titled, ‘Referring toggplot2functions’↩︎See the section titled, ‘Confusion about Imports’ in R Packages, 2ed, “Listing a package in
ImportsinDESCRIPTIONdoes not ‘import’ that package.”↩︎Users can access functions from add-on packages with the
pkg::funsyntax.↩︎“Our recommended default is to call external functions using the
package::function()syntax.” - R Packages, 2ed↩︎The namespace controls the search strategy for variables used by functions in the package. If not found locally, R searches the package namespace first, then the imports, then the base namespace and then the normal search path (so the base namespace precedes the normal search rather than being at the end of it). - Writing R Extensions↩︎
“Using
importFromselectively rather thanImportsis good practice and recommended notably when importing from packages with more than a dozen exports and especially from those written by others (so what they export can change in future).” - Specifying imports and exports.↩︎“Specifically, we recommend that you default to not importing anything from [add-on packages] into your namespace. This makes it very easy to identify which functions live outside of your package, which is especially useful when you read your code in the future. This also eliminates any concerns about name conflicts between [add-on packages] and your package.” - R Packages, 2ed↩︎
See the Function lookup inside a package section of R Packages, 2ed↩︎
See the Package environments and the search path of Advanced R, 2ed↩︎
See section 1.1.3 Package Dependencies in Writing R Extensions↩︎
See section 1.5, Package namespaces in Writing R Extensions↩︎












