00:00:00.000
welcome to this session I'm going to
00:00:02.159
talk about how to build an offline
00:00:04.500
experience with the rails powered pwa
00:00:09.500
so a bit about me I'm a software
00:00:12.179
developer at Dallas labs this is my
00:00:14.940
part-time job I'm actually a music
00:00:16.859
composer the other half of my productive
00:00:20.279
time
00:00:22.100
I was a natural resources engineer
00:00:25.380
before this so that's where my passion
00:00:27.900
about technology and sustainability
00:00:29.820
comes from
00:00:31.199
and actually the case study that I'm
00:00:33.600
gonna talk about it's it comes from from
00:00:36.300
that background
00:00:38.219
so as a quick upper view of this
00:00:40.680
presentation
00:00:42.180
I'm gonna Define first what is a blue UA
00:00:45.780
and why I think they're awesome then I'm
00:00:48.059
gonna briefly cover the case study
00:00:51.059
and then I'm gonna explain the main two
00:00:54.420
assets that are required to turn your
00:00:56.879
rails application into a progressive web
00:00:59.760
app
00:01:00.780
and then the substance the main
00:01:03.600
substance of this presentation which is
00:01:05.640
how to perform offline uh create read
00:01:09.299
update and delete quote-unquote actions
00:01:13.920
and finally some key takeaways
00:01:17.100
so
00:01:18.240
uh appear away is an acronym that stands
00:01:21.600
for Progressive web applications so
00:01:24.720
these are just web apps that use
00:01:27.299
service workers and manifests which are
00:01:30.119
two assets that I'll explain later and
00:01:32.880
other web platforms features in
00:01:35.460
combination with Progressive enhancement
00:01:37.439
to give users a boosted experience on
00:01:41.159
pair with Native applications
00:01:43.320
so this enhanced capability can be such
00:01:47.520
as speech performance push notifications
00:01:50.720
or in the case of this case study the
00:01:55.560
offline support So this gives pillow UA
00:01:59.579
um
00:02:00.720
enhanced capabilities reliability and
00:02:03.659
installability
00:02:05.759
so I like to think about pwas as the mix
00:02:09.300
of best of the Native and web Rewards
00:02:13.500
so this is a common chart that I've seen
00:02:15.980
in the web
00:02:18.000
I've seen some versions of the chart
00:02:20.340
where pwas and Native applications are
00:02:23.160
kind of the same level but I think
00:02:26.040
um
00:02:26.700
that because native apps are so platform
00:02:29.819
specific
00:02:32.160
they can reach more capabilities than
00:02:35.640
people always but still I think people
00:02:37.500
always are a pretty good deal
00:02:40.020
in terms of uh the budget friendliness
00:02:43.640
uh you can install them but it's not
00:02:46.500
required so you can just use them from
00:02:48.660
the regular browser
00:02:50.540
you can have search engine visibility
00:02:53.580
which you don't enter in in Native
00:02:56.459
applications
00:02:58.080
and so on so I think this is a pretty
00:03:01.319
good deal for a web application
00:03:05.160
so
00:03:06.360
the case study
00:03:08.640
um this was an application that was
00:03:10.800
requested by the Italian government and
00:03:13.019
it was meant to be used by farmers and
00:03:15.420
farm technicians in the field in order
00:03:18.720
to diagnose their fields uh sorry their
00:03:21.840
yeah their farms in terms of
00:03:23.819
sustainability so it was
00:03:26.580
uh kind of a complex survey that they
00:03:29.519
will fill up and they will show the
00:03:31.440
results to the farmers in the field
00:03:33.780
so as you can imagine uh the offline
00:03:36.540
support it was very important because
00:03:39.780
uh because we're talking about farms and
00:03:42.420
rural areas where internet connection is
00:03:44.459
not very reliable
00:03:47.220
so uh we really needed to tackle this
00:03:49.920
this challenge uh fast and in a
00:03:52.739
maintainable way
00:03:54.360
so this is uh the way it looks it's it's
00:03:57.900
a complex survey it has it has file
00:04:00.420
uploads Maps
00:04:03.120
um lots of questions
00:04:05.879
so we needed to provide a way for this
00:04:08.940
farmer and farm technicians to fill this
00:04:11.340
form offline
00:04:13.019
so the solution we came up with was to
00:04:16.199
build
00:04:18.919
an application with our favorite
00:04:21.359
favorite framework which is Ruby on
00:04:23.940
Rails so we could just provide value as
00:04:27.660
fast as possible to our client and then
00:04:30.479
we would progressively enhance it with
00:04:33.240
the pwa features
00:04:36.300
um we considered to build a native
00:04:38.340
application but this would require both
00:04:40.620
a rails API and the native code so this
00:04:43.979
would disallow a web experience without
00:04:46.919
an additional web front end so we wanted
00:04:49.919
to have a single code base
00:04:52.139
and efficiency and maintainability were
00:04:55.080
really key because of the budget
00:04:57.180
constraints
00:04:59.940
so um I'm kind of a homesick now so this
00:05:03.120
is just all images from from Chile where
00:05:06.540
I come from
00:05:07.740
uh so how to turn your application into
00:05:10.740
a video uh the Main Ingredients so these
00:05:14.699
are two assets
00:05:16.620
the first is the service worker
00:05:20.160
so
00:05:21.479
um the official definition is that
00:05:24.479
service workers are a specialized
00:05:26.400
JavaScript assets that acts as proxies
00:05:29.280
between your web browser and the web
00:05:32.160
server
00:05:33.120
so there are like in the middle of them
00:05:35.720
intercepting requests and improving
00:05:38.660
reliability boosting page performance
00:05:41.520
like all sorts of enhanced capabilities
00:05:45.180
but the key part here is that service
00:05:47.940
workers are an enhancement so there
00:05:52.020
might be cases where your client does
00:05:54.360
not have browser support for service
00:05:56.520
worker but this means that no base
00:05:59.699
functionality should be broken because
00:06:01.680
they're again an enhancement
00:06:04.020
so I like to think about service workers
00:06:06.479
like a small application that's running
00:06:08.400
in parallel to your rails application
00:06:10.560
and it's kind of there in the middle
00:06:12.900
intercepting requests and doing
00:06:15.419
different kinds of things
00:06:17.460
so the scope is important uh it should
00:06:19.919
be available at the root scope because
00:06:22.500
this is
00:06:24.060
this is reflected in the scope that your
00:06:27.300
service worker will be capable to
00:06:29.759
control
00:06:30.960
so we wanted to be at the roots code so
00:06:33.240
it can control all of your all the pages
00:06:35.639
in within your your main
00:06:39.360
web application
00:06:41.759
so this is uh kind of an example uh this
00:06:45.000
is not like an actual service worker it
00:06:47.699
look like but I wanted to show
00:06:50.880
uh these callbacks so the service worker
00:06:54.180
Works based on a life cycle of events
00:06:57.840
that are triggered in the browser and
00:07:00.360
the service worker can listen to them so
00:07:02.460
that's why I added those event listeners
00:07:05.639
at the end and every time it hears or it
00:07:08.580
listens for one of these events it
00:07:11.340
triggers this callback
00:07:13.620
so you fortunately don't have to write
00:07:16.560
these callbacks uh by yourself I
00:07:19.740
recommend to use a library that's called
00:07:22.139
workbox it's the link it's at the end of
00:07:24.900
this presentation which allows a more
00:07:27.419
programmer friendly Syntax for writing
00:07:30.419
your service worker
00:07:33.060
so the second main ingredient here is
00:07:36.060
the app manifest
00:07:37.560
so this is a Json file that will tell
00:07:40.800
your browser how your pillow UI should
00:07:43.680
display within the operating system of
00:07:46.259
your user's device
00:07:47.819
so this is an important file to make
00:07:49.800
your app installable
00:07:51.660
and that's to make it look and feel like
00:07:54.240
a native application
00:07:56.039
and same thing here with the with the
00:07:58.620
scope it should be available at the root
00:08:01.199
scope
00:08:03.000
so this is uh more or less how a
00:08:06.000
manifest look like
00:08:07.620
in this case I used
00:08:10.160
json.erb because I want to do some of
00:08:12.599
the Rails helpers
00:08:14.400
so I could use for example display my
00:08:17.940
home screen icon which is the icon that
00:08:21.599
will be added to the home screen of your
00:08:24.539
user's device when they accept the
00:08:28.199
prompt to install the application I
00:08:30.360
don't know if any of you have ever enter
00:08:32.760
your website and you're prompted to add
00:08:35.520
the home screen icon so that's probably
00:08:38.459
because these applications have a web
00:08:40.320
manifest
00:08:42.419
and other things such as the
00:08:46.020
like the display and the color and here
00:08:50.040
the the kind of web browser display that
00:08:52.920
you want to choose I chose Standalone
00:08:55.019
because I wanted to resemble as much as
00:08:57.000
possible native experience
00:09:01.320
so I'm not gonna dig into how to add
00:09:03.600
these files in your application because
00:09:05.339
I wrote I wrote a very detailed blog
00:09:08.279
post on this topic and I'd have little
00:09:10.680
time so I just left the link there but I
00:09:14.760
want to dig into how to create records
00:09:17.880
in your application and sync them later
00:09:22.080
so there are two main apis I used here
00:09:25.920
the first one is index DB so in terms of
00:09:30.420
browser storage tools we have different
00:09:32.880
options we have cookies session storage
00:09:35.760
we have local storage
00:09:38.100
and we have index DB
00:09:40.560
I chose this one because it's very
00:09:42.720
powerful you can store pretty much
00:09:45.240
everything from like Json objects to
00:09:49.260
even files blobs images
00:09:53.100
um it has persistence across sessions
00:09:55.680
and across tab browser tabs so that's
00:09:58.800
very powerful
00:10:01.080
and
00:10:03.120
um it's well it's a JavaScript API uh it
00:10:06.660
relies heavily on premises so it's
00:10:08.760
recommended that you have uh an at least
00:10:12.180
basic understanding of promises uh when
00:10:15.300
you deal with this but even if you do
00:10:17.820
have a strong Foundation I recommend
00:10:19.980
using a promise sorry a wrapper I
00:10:23.040
promise it will be easier
00:10:25.140
so wrappers RGS libraries that allow a
00:10:27.720
more programmer friendly Syntax for
00:10:29.760
using indexeddb under common different
00:10:31.860
flavors different functionalities
00:10:35.160
so you don't have to deal with these
00:10:37.200
promise syntax
00:10:38.700
um like
00:10:40.320
playing
00:10:42.240
so the other tool is the background Sync
00:10:45.300
API and this is a very powerful tool
00:10:48.240
that allows web applications to defer
00:10:50.160
service synchronization work to their
00:10:52.200
service worker so it can be handled at a
00:10:55.260
later time so the browser we basically
00:10:58.019
detect when the connection is back
00:11:02.100
and it will trigger the background
00:11:04.019
synchronization
00:11:06.240
so the implementation works more or less
00:11:08.820
like this
00:11:10.079
we have our server on the left side
00:11:12.540
sorry on the right side and our client
00:11:15.660
in the left side
00:11:17.220
let's suppose we have a user that's uh
00:11:20.160
just completed a survey and they want to
00:11:22.079
make a post request to your server
00:11:25.079
but uh the user is not connected to the
00:11:27.660
internet so this post request will fail
00:11:31.019
so this is where indexeddy comes to play
00:11:34.200
we will catch this object and store it
00:11:38.100
in index DB
00:11:39.839
so the the entire survey that your
00:11:42.480
farmer or your user completed it's not
00:11:45.480
stored in index DB
00:11:47.519
and then the background thing will later
00:11:49.800
detect when your user is back online and
00:11:54.000
will attempt the request again taking
00:11:56.279
the object from index DB
00:11:58.680
and now we have a synced server
00:12:03.480
so in order to do this we need to
00:12:05.640
perform several steps we first need to
00:12:08.100
check the network status so for this uh
00:12:11.760
the simplest solution that we found or
00:12:15.000
better the most effective uh it was
00:12:18.899
through appalling so we had this one
00:12:21.420
pixel image stored in our server we
00:12:23.880
would exclude this image from the
00:12:26.160
caching Passover service worker so this
00:12:28.680
image would never be cached it will
00:12:30.600
always be fetched fetched from the
00:12:33.480
server
00:12:34.760
and we every every time we would do this
00:12:37.920
like every 10 15 seconds we would store
00:12:41.279
a Boolean value value in our local
00:12:43.680
storage that we would use as a source uh
00:12:46.920
source of Truth to know whether we are
00:12:49.079
online or not
00:12:51.660
um then we need to declare or find uh an
00:12:54.720
already created database with our
00:12:57.360
wrapper for indexeddb
00:13:00.779
so every time we would submit a form
00:13:03.660
we would check the network status and if
00:13:06.180
no network was available we will store
00:13:08.639
this object in indexeddy instead of
00:13:11.880
Performing the actual post request
00:13:14.639
so as a productive here uh very quickly
00:13:16.920
we found out that this uh options we we
00:13:21.899
were performing in different places
00:13:24.480
um so in order to not to repeat
00:13:26.399
ourselves we ended up using stimulus
00:13:29.160
mixing so we would have a mixing for
00:13:31.320
checking the network status we would
00:13:33.360
have another for declaring a database
00:13:38.100
so um the implementation of the actual
00:13:41.339
function function to perform the
00:13:43.740
background sync
00:13:45.600
looks more or less like this we would
00:13:48.600
first register the the event under a
00:13:52.500
certain tag
00:13:53.880
then we would match this tag with the ad
00:13:57.180
event listener sorry with the event
00:13:59.100
listener that we
00:14:00.980
registered for this sync event
00:14:04.980
so every time we would trigger this we
00:14:08.160
would listen this event we would trigger
00:14:09.959
this function which is sync service
00:14:12.779
so in this case I'm using my jaxi syntax
00:14:16.860
like C is the wrapper that I I chose
00:14:21.180
so
00:14:22.860
um we would first query the database to
00:14:26.579
know whether there are service or not
00:14:28.500
and if we find service we would store
00:14:32.279
them in an array
00:14:33.779
we would declare an another array to
00:14:37.320
that I will explain later which is
00:14:39.779
service ID stream of
00:14:41.699
as an empty array
00:14:44.399
so we would then
00:14:46.620
iterate through this array and perform
00:14:49.380
an Ajax request to your server
00:14:52.440
for each one of them
00:14:54.360
and if the response was successful so we
00:14:57.300
make a post request for a survey and we
00:14:59.820
get a successful response so we take
00:15:02.940
that ID and we push it to this ID to
00:15:06.360
remove array
00:15:08.399
and we will look to through all our
00:15:11.339
service and finally we would uh when all
00:15:16.019
uh surveys were already synced we would
00:15:19.380
Loop through the service IDs to remove
00:15:21.779
to remove those objects from indexedd
00:15:25.920
because once we synced a surfy with the
00:15:30.240
server we no longer need that object in
00:15:32.760
index DB
00:15:36.480
so
00:15:38.100
um thanks Laurie
00:15:40.639
what if we want to enable manual
00:15:44.100
synchronization so there are a number of
00:15:46.680
reasons why a user would want to have
00:15:49.680
more control over when this happens
00:15:52.440
because background sync as the name says
00:15:55.019
occurs in the background so the user has
00:15:57.660
no control over when this happens or
00:16:00.660
under which conditions
00:16:02.579
so
00:16:03.959
um we might have for example a failsync
00:16:06.600
or the fact that some browsers don't
00:16:09.660
support background sync because for
00:16:12.540
example some privacy centered
00:16:15.120
uh browsers such as Brave that does have
00:16:18.660
it but they have a disabled by default
00:16:21.180
so
00:16:22.440
um among other reasons we wanted to give
00:16:25.980
our users more control over when this
00:16:28.320
synchronization happens
00:16:30.980
so in order to do this we used stimulus
00:16:34.800
again
00:16:36.720
so the way we implemented this was
00:16:39.779
connecting a stimulus controller to a
00:16:43.740
sync button that we will use for example
00:16:46.019
in our index so we would have our our
00:16:49.199
index of service and we would have there
00:16:51.660
a button that would say like sync and
00:16:55.800
that button would be connected to a
00:16:58.320
stimulus controller that would perform
00:17:00.540
the same operation that I just showed
00:17:02.880
you
00:17:03.540
so the the previous function was in the
00:17:05.939
service worker but we could use the same
00:17:08.280
function in a stimulus controller to
00:17:10.260
trigger it manually
00:17:13.260
so this is about the create right but
00:17:16.319
what do we do if we want to read this uh
00:17:19.860
data so
00:17:21.900
we just created an object we stored it
00:17:24.959
in index TV and this is how it looks but
00:17:27.240
we don't want our users to go to the
00:17:29.160
developer tools window to see this
00:17:32.700
object right
00:17:33.780
so we went a way to display this
00:17:37.080
information to the user in a further way
00:17:41.820
so again here we use the stimulus to
00:17:44.340
read these indexeddb data
00:17:47.039
and then we use uh
00:17:51.179
a library called mustache to render this
00:17:55.140
data
00:17:56.220
in the HTML that the user can see so for
00:18:00.059
each
00:18:00.900
survey that we would find we would
00:18:02.820
iterate and then perform a JavaScript
00:18:06.480
function to add this item to the Dom
00:18:10.500
so for this case HTML templates are your
00:18:13.620
friends in case of yeah you don't know
00:18:16.140
this HTML templates are a special tag
00:18:18.860
that is rendered but not actually
00:18:21.960
displayed so when you're
00:18:25.140
user is online it will render the page
00:18:28.559
this template would be loaded and then
00:18:31.260
we can use stimulus and mustache to
00:18:33.720
bring this template populated and then
00:18:37.020
render it to the user with the
00:18:39.539
information taken from index DB
00:18:42.179
so this is how the the syntax looks like
00:18:45.360
this is a template tag and we're using
00:18:47.820
here Master syntax to pass then the
00:18:51.000
attributes from the object that we
00:18:52.799
stored in indexeddb
00:18:54.840
foreign
00:18:58.760
to populate the template and render it
00:19:03.360
this is how the function looks like and
00:19:06.539
I'm kind of running out of time so I'm
00:19:08.760
just gonna skim through this
00:19:10.919
uh this is how the
00:19:13.620
uh the index look like so this is the
00:19:16.320
button I just mentioned uh with this
00:19:19.080
button would be connected to stimulus so
00:19:20.820
we could trigger a manual
00:19:21.900
synchronization we have a trouble here
00:19:24.660
that would show us uh this the network
00:19:27.660
status this would use the local storage
00:19:31.320
value that I just mentioned as a source
00:19:34.080
of Truth
00:19:35.340
we would have this
00:19:37.740
um icons to tell the users whether these
00:19:42.120
items are synced or not
00:19:46.100
and yeah pretty much pretty much that
00:19:49.980
um same here with the with the update we
00:19:52.919
would use a template version of the
00:19:56.220
survey like the entire Forum we would
00:19:59.460
put it between template tags so when the
00:20:02.220
user clicks this button that template
00:20:04.500
version would come up rendered uh using
00:20:07.919
the values from indexeddb
00:20:12.140
uh so yeah pretty much what what I just
00:20:15.179
explained
00:20:17.820
um so some gotchas here
00:20:20.520
um one important thing is that
00:20:22.620
validations must match in the front end
00:20:25.380
and the back end because we don't want
00:20:27.840
uh invalid objects to be stored in
00:20:31.080
indexeddb because that would cause uh a
00:20:34.200
failure when making this synchronization
00:20:36.780
so we want every object stored in index
00:20:41.039
DB to be a valid object in active record
00:20:44.820
so this kind of kind of makes uh the
00:20:48.120
challenge of duplicating some of the
00:20:50.580
validations but it's really important
00:20:53.100
and the other thing is that it's really
00:20:55.140
important to understand
00:20:56.720
your uh audience to assess the
00:20:59.520
importance of this browser compatibility
00:21:01.500
for some of the features that I
00:21:03.900
mentioned in dxb platform background
00:21:06.000
sync because there are different amount
00:21:09.360
differences among like Android users and
00:21:12.660
iOS users
00:21:14.280
and depending of your audience's most
00:21:16.679
used browser you might have to assess
00:21:18.900
the compatibility
00:21:23.039
um so that was it I hope this was a
00:21:28.320
well I hope this this was a meaningful
00:21:31.500
presentation so PW is features can
00:21:34.260
supercharge your application you can
00:21:36.360
make suitable for anyone everywhere uh
00:21:39.900
you can start making an impact by
00:21:42.120
reaching unconventional audiences such
00:21:44.700
as people uh in rural areas
00:21:48.440
and finally stimulus is a super powerful
00:21:51.900
tool for enhancing our application with
00:21:53.940
offline features
00:21:55.440
um you you can pretty much emulate a
00:21:57.600
single page application Behavior with
00:22:00.240
like minimum uh Js
00:22:04.679
and these are these are some of the
00:22:06.659
resources I mentioned
00:22:08.640
and that was it thank you very much