+ - 0:00:00
Notes for current slide
Notes for next slide

{shinyMobile} Hands-on

David Granjon

2021-07-09

Hosted by

1

Hi there πŸ•

We're in for 2 hours of fun!

  • Grab a β˜•
  • Make yourself comfortable πŸ›‹ or 🧘
  • Ask questions ❓


David

Senior Expert Data Scientist, Novartis

2

Program

Mobile app development with {shiny}, {golem} et {shinyMobile}

Learning objectives:

  • πŸ‘©β€πŸ« State of the art.
  • πŸ‘¨β€πŸ« {shinyMobile} introduction: components, templates, themes ...
  • Setup a production app.

Discover progressive web apps (PWA) and create a simple example, built on top the previous part.

Learning objectives:

  • Discover what is a progressive web app.
  • Step by step development.
  • Overview of what are the next steps and limitations.
3

Workshop Material

Prerequisites

  • Preliminary experience with {shiny}.
  • Basic knowledge about R packages may help.
  • Be curious.
4

Part 1.1 Introduction to {shinyMobile}

5

Mobile development


Classic web apps are not optimized for mobile platforms.

  • Rarely consider the small display.
  • Harder to access (no launch icon).
  • Harder to focus if not fullscreen.
  • Don't work offline.


6

Mobile development


Classic web apps are not optimized for mobile platforms.

  • Rarely consider the small display.
  • Harder to access (no launch icon).
  • Harder to focus if not fullscreen.
  • Don't work offline.


Appearance is critical for end user. πŸ’‡

6

Mobile development


😈 Let's go native!

Well ... it's quite complex:

  • Know multiple languages: Java, Swift, ...
  • Maintain multiple code bases.


7

Mobile development


😈 Let's go native!

Well ... it's quite complex:

  • Know multiple languages: Java, Swift, ...
  • Maintain multiple code bases.


❓ Isn't there something simpler?

7

Progressive web apps (PWA)


Take some advantages of both worlds:

  • Can be installed on the device ...
  • ... But run via a web browser.
  • Provide offline features (don't expect too much).
  • One code base (web languages).


8

Progressive web apps (PWA)


Take some advantages of both worlds:

  • Can be installed on the device ...
  • ... But run via a web browser.
  • Provide offline features (don't expect too much).
  • One code base (web languages).


😭 Less performance than native.

8

Mobile app development and {shiny}?


Let's be honest, there is almost nothing 😭.

  • Many shiny apps are not optimized for mobile.
  • Vertical design.
  • Overflow.
  • Poor user experience.
9

Mobile app development and {shiny}?


{miniUI} exists but not really for mobile development.

10

Welcome {shinyMobile}?


Built on top of the Framework7 web framework.

  • Native look and feel for iOS and Android
  • PWA support.
  • ... also works for desktop apps 😏

11

Key features: Layout

  • f7SingleLayout(): one page app.
  • f7TabLayout(): multi-tabs app.

12

Key features: Skins

  • iOS.
  • Android.



13

Key features: Themes

2 themes:

14

Demo app: {deminR}

15

Your turn πŸ§ͺ!

16

Part 1.2 Project setup

17

Initialize the project: {truelle}


We'll be using {golem} ...

18

Initialize the project: {truelle}


We'll be using {golem} ...

... {truelle} is a {golem} GUI generator πŸ§™β€β™‚οΈ!

  1. Open the RStudio Server.
  2. Run truelle::run_app().
  3. Follow the steps.
18

{truelle}: project type


Select Package and choose the {golem} engine.

19

{truelle}: Package options


Provide a valid package path and review project options.

20

{truelle}: UI template


Select {shinyMobile} tabs layout.

21

{truelle}: Run the output and enjoy 😎!


Click on the ⏯ button or copy/paste πŸ“Έ the code to your terminal...

Congrats 🎈! We now have a production ready {shinyMobile} project.

22

Part 1.3 {shinyMobile}'s main features

23

{shinyMobile}: App preview


Your turn πŸ§ͺ

  • {shinyMobile} allows app preview (local or remote).
  • Remote preview:
shinyMobile::preview_mobile(
url = "https://connect.thinkr.fr/shinyMobile1/",
device = "iphone8"
)
24

{shinyMobile}: App preview


Your turn πŸ§ͺ

  • {shinyMobile} allows app preview (local or remote).
  • Remote preview:
shinyMobile::preview_mobile(
url = "https://connect.thinkr.fr/shinyMobile1/",
device = "iphone8"
)
  • Open the new project.
  • Run devtools::load_all().
  • Run the app in a separate R process (only works with local RStudio):
processx::process$new(
"Rscript",
c("-e",
sprintf(
"thematic::thematic_shiny();
shiny::runApp('%s', port = %s)",
"./inst/app", 3434
)
)
)
shinyMobile::preview_mobile(
url = "http://localhost:3434",
device = "iphone8"
)

24

{shiny} VS {shinyMobile}: Main UI wrapper


library(shiny)
ui <- fluidPage(
# ui logic
)
server <- function(input, output, session) {
# server logic
}
shinyApp(ui, server)
25

{shiny} VS {shinyMobile}: Main UI wrapper


library(shiny)
ui <- fluidPage(
# ui logic
)
server <- function(input, output, session) {
# server logic
}
shinyApp(ui, server)

library(shiny)
library(shinyMobile)
ui <- f7Page(
# shinyMobile layout functions
)
server <- function(input, output, session) {
# server logic
}
shinyApp(ui, server)
25

{shinyMobile}: app options


  • f7Page() exposes an options parameter.
  • Fine tune the app appearance and behavior:
    • Theme, colors, ...
    • Dark mode (default to TRUE).
    • tapHold (WHAAAAT???).
    • navbar and toolbar options.
    • ...
  • Options inherited from Framework7.
26

{shinyMobile}: app options


  • f7Page() exposes an options parameter.
  • Fine tune the app appearance and behavior:
    • Theme, colors, ...
    • Dark mode (default to TRUE).
    • tapHold (WHAAAAT???).
    • navbar and toolbar options.
    • ...
  • Options inherited from Framework7.

Your turn πŸ₯Ό

  • Inspect the ./R/app_ui.R code and experiment with some of the options.
  • Run devtools::load_all() and run_app().
26

{shinyMobile}: the tab layout


We leverage the f7TabLayout() function:

f7TabLayout(..., navbar, messagebar = NULL, panels = NULL, appbar = NULL)
  • ... are the tabs contained in f7Tabs().
  • f7Tabs() may host multiple f7Tab().
  • navbar expects an f7Navbar().
  • We don't care about other parameters for now.
27

{shinyMobile}: the tab layout


We leverage the f7TabLayout() function:

f7TabLayout(..., navbar, messagebar = NULL, panels = NULL, appbar = NULL)
  • ... are the tabs contained in f7Tabs().
  • f7Tabs() may host multiple f7Tab().
  • navbar expects an f7Navbar().
  • We don't care about other parameters for now.

Your turn πŸ§ͺ

  • Inspect the ./R/app_ui.R code and find the corresponding pieces.
  • Add a new f7Tab() element. Hint: f7Tab(..., tabName, icon = NULL, active = FALSE, hidden = FALSE).
  • Run devtools::load_all() and run_app().
27

{shinyMobile}: the navbar


f7Navbar():

  • Contains title, subtitle.
  • Optional subNavbar with f7SubNavbar().
  • Other parameters offer cosmetic options:
    • shadow.
    • horizontal line.
    • transparent.
    • ...
28

{shinyMobile}: the navbar


f7Navbar():

  • Contains title, subtitle.
  • Optional subNavbar with f7SubNavbar().
  • Other parameters offer cosmetic options:
    • shadow.
    • horizontal line.
    • transparent.
    • ...

Your turn πŸ₯Ό

  • In ./R/app_ui.R, modify the navbar title.
  • Add a subtitle.
  • Add a f7SubNavbar() such as:
f7SubNavbar(
f7Button(label = "My button"),
f7Button(label = "My button"),
f7Button(label = "My button")
)
  • Turn the leftPanel and rightPanel to FALSE.
  • Set hairline to FALSE.
  • Run devtools::load_all() and run_app().
28

{shinyMobile}: components


{shinyMobile} exposes many components:

  • Containers: f7Card(), f7List(), f7Block(), ...
  • Inputs: f7Text(), f7Checkbox(), f7Toggle(), ...
  • Notifications: f7Notif(), f7Dialog(), f7ActionSheet(), ...
  • Buttons: f7Button(), f7TabLink(), ...
  • Metric: f7Gauge(), f7Progress(), ...

Most of them can be updated server side!

29

{shinyMobile}: components


{shinyMobile} exposes many components:

  • Containers: f7Card(), f7List(), f7Block(), ...
  • Inputs: f7Text(), f7Checkbox(), f7Toggle(), ...
  • Notifications: f7Notif(), f7Dialog(), f7ActionSheet(), ...
  • Buttons: f7Button(), f7TabLink(), ...
  • Metric: f7Gauge(), f7Progress(), ...

Most of them can be updated server side!

Your turn πŸ§ͺ

  • In ./R/app_ui.R, add a new f7ExpandableCard() in the previously added f7Tab().
  • Give it an id.
  • Below the card add a f7Button("openCard", "Click me!").
  • In ./R/app_server.R add this code, replacing <CARD_ID>:
observeEvent(input$openCard, {
updateF7Card(id = <CARD_ID>)
})
  • Run devtools::load_all() and run_app().
29
30

Part 2.1 Introduction to PWA

31

Progressive web apps


Progressive web apps or (PWA) improve classic web apps capabilities by obeying these three rules:

  1. Being capable: media control, file system access, …
  2. Being reliable: fast and usable offline. Transparent failures.
  3. Being installable: Standalone use, launch from user’s home screen.

In the following we'll treat 2 and 3.

32

How can my app be installable?


  • The app must be served over HTTPS (shinyapp.io, ...).
  • Include a manifest, that is a JSON file specifying app metadata (name, icons, ...).
  • Have a registered service worker, which can cache the app content and provide offline support.

33

What is a web manifest?


A JSON file which can be interpreted by the web browser (Chrome, Safari, Firefox, Brave, ...).

{
"name": "My Progressive Web App",
"short_name": "My App",
"description": "What it does!",
"lang": "en-US",
"start_url": "https://dgranjon.shinyapps.io/shinyMobileGolemTest/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#ffffff",
"icons": [
{
"src": "icons/icon-144.png",
"sizes": "144x144"
}
],
...
}

Most important fields:

  • start_url: where your app is hosted.
  • display: use standalone for native look and feel.
  • icon: an array pointing to icons (useful for favicon, launch screen ...) in the www folder.

Check compatibility support here.

In a Shiny app context, the manifest goes in www.

34

The service worker


Script run in the background by the web browser.

self.addEventListener("install", (event) => {
event.waitUntil(
(async () => {
const cache = await caches.open(CACHE_NAME);
// Setting {cache: 'reload'} in the new request will ensure that the
// response isn't fulfilled from the HTTP cache; i.e., it will be from
// the network.
await cache.add( new Request(OFFLINE_URL, { cache: "reload" }) );
await cache.add( new Request("framework7-5.7.14/css/framework7.bundle.min.css", { cache: "reload" }) );
// add other resources to cache below
})()
);
// Force the waiting service worker to become the active service worker.
self.skipWaiting();
});

Features:

  • Requires HTTPS.
  • Able to store app resources (CSS, JS, images, HTML, ...) in the application cache ...
  • ... Thereby making them available offline.
  • Many plug and play template here and there.

Script composed of 3 steps:

  • Installation + cache configuration (see left code).
  • Activation.
  • Fetch requests (fill the cache, ...)
35

Part 2.2 Develop a PWA with {shinyMobile}

36

Road to PWA : enable PWA


Good new πŸ˜ƒ!

37

Road to PWA : enable PWA


Good new πŸ˜ƒ!

{shinyMobile} is PWA ready 😎.

37

Road to PWA : enable PWA


Good new πŸ˜ƒ!

{shinyMobile} is PWA ready 😎.

Just turn on the allowPWA to TRUE in f7Page(). Under the hood, it adds all the required machinery πŸ§™.

37

Road to PWA : Modify {golem} package


Your turn πŸ₯Ό

  • Open ./R/app_ui.R.
  • Scroll down to the golem_add_external_resources function.
  • Comment out the code as shown on the left (to avoid conflicts with {shinyMobile} PWA assets).
  • Call golem::add_ui_server_files().
golem_add_external_resources <- function(){
#add_resource_path(
# 'www', app_sys('app/www')
#)
tags$head(
#favicon(),
#bundle_resources(
# path = app_sys('app/www'),
# app_title = 'shinyexample'
#)
# Add here other external resources
# for example, you can add shinyalert::useShinyalert()
)
}
38

Road to PWA : add PWA files (1/2)


Time consuming, need automated approach... The below script:

39

Road to PWA : add PWA files (1/2)


Time consuming, need automated approach... The below script:

# Important to target the app folder.
# Only work if app is in a package.
# register_service_worker is FALSE, shinyMobile already does it.
# We also don't need to create web dependencies.
charpente::set_pwa(
"inst/app",
register_service_worker = FALSE,
create_dependencies = FALSE
)
39

Road to PWA : add PWA files (1/2)


Time consuming, need automated approach... The below script:

# Important to target the app folder.
# Only work if app is in a package.
# register_service_worker is FALSE, shinyMobile already does it.
# We also don't need to create web dependencies.
charpente::set_pwa(
"inst/app",
register_service_worker = FALSE,
create_dependencies = FALSE
)
  • Creates a web manifest.
  • Adds a service worker.
  • Adds placeholder icons (you may need to create your own later).
  • Adds an offline HTML template page.
39

Road to PWA: add PWA files (2/2)


Your turn πŸ§ͺ

  • Run charpente::set_pwa("inst/app", register_service_worker = FALSE, create_dependencies = FALSE) inside your project.
  • Run devtools::load_all() and run_app().
  • Once the RStudio viewer opens, click on Open in Browser.
  • Note the app URL: https://baka.thinkr.fr/<CONTAINER_ID>/rstudio/p/<RSTUDIO_TOKEN>/.
  • Copy it in the ./inst/app/www/manifest.webmanifest start_url field.


This workshop is hosted by ThinkR on a dedicated server. If you are working locally, first deploy your app to a secure server (with HTTPS like shinyapps.io). PWA features don't work locally!!!

40

Road to PWA: Check your app (1/3)


Your turn πŸ§ͺ

  • App is already running in Chrome.
  • If App does not work: use https://dgranjon.shinyapps.io/shinyMobileGolemTest.
  • Open the Chrome developer tools: βŒ₯ + ⌘ + I (Mac), ctrl + shift + I (windows).
  • Follow the steps showed in the figure (in red).
  • Click on Generate report and wait.


41

Road to PWA: Check your app (2/3)


We aim to see the installable property in green.

42

Road to PWA: Check your app (3/3)


Your turn πŸ§ͺ

  • Open the Chrome developer tools: βŒ₯ + ⌘ + I (Mac), ctrl + shift + I (windows).
  • Follow the steps showed in the figure (in red).
  • Check that offline.html and JS/CSS files are cached (necessary for offline support).


43

Road to PWA : handle the installation


Desktop

iOS

44

Road to PWA: play around


Your turn πŸ₯Ό

  • Install the deployed app.
  • Create a desktop shortcut.
  • Launch the app.
  • Emulate disabled network connection (Don't shutdown your wifi! See figure πŸ˜†).
  • Reload the page and observe.
  • Re-activate internet and reload the app.


45

Road to PWA: play around


Your turn πŸ₯Ό

  • Install the deployed app.
  • Create a desktop shortcut.
  • Launch the app.
  • Emulate disabled network connection (Don't shutdown your wifi! See figure πŸ˜†).
  • Reload the page and observe.
  • Re-activate internet and reload the app.


Installed apps can be accessed under chrome://apps/

45

Road to PWA: What's next?


  • Improve the offline HTML page.
  • Take this course.
  • PWA shortcuts (Windows, Android support).
46
47

Hi there πŸ•

We're in for 2 hours of fun!

  • Grab a β˜•
  • Make yourself comfortable πŸ›‹ or 🧘
  • Ask questions ❓


David

Senior Expert Data Scientist, Novartis

2
Paused

Help

Keyboard shortcuts

↑, ←, Pg Up, k Go to previous slide
↓, β†’, Pg Dn, Space, j Go to next slide
Home Go to first slide
End Go to last slide
Number + Return Go to specific slide
b / m / f Toggle blackout / mirrored / fullscreen mode
c Clone slideshow
p Toggle presenter mode
t Restart the presentation timer
?, h Toggle this help
oTile View: Overview of Slides
Alt + fFit Slides to Screen
Esc Back to slideshow