Intro
In this post, we'll explore how to implement a service worker in a Rails 7 application to enable offline functionality. You’ll learn how to cache important assets such as CSS, JavaScript, and images, as well as handle network requests to ensure your app remains functional even without an internet connection. We'll walk through the key steps, from setting up the service worker to testing its offline capabilities, ensuring a seamless experience for users.
What Are Service Workers?
Service workers are background scripts that run in the browser, acting as a proxy between the web app and the network. They enable features like offline support, background synchronization, and push notifications. By intercepting network requests and caching responses, service workers allow apps to load faster and function even when the user is offline. They are crucial for improving performance and reliability, especially in Progressive Web Apps (PWAs).
Step 1: Set Up Your Service Worker in Rails 7
The first step in integrating a service worker into your Rails 7 app is to configure a route to serve the service worker file and manifest file. You’ll also need to set up the necessary files in your Rails app.
1. Create the Route for the Service Worker and Manifest:
In your config/routes.rb, define routes for the service worker and manifest files:
Rails.application.routes.draw do
get "/service_worker.js" => "service_worker#service_worker"
get "/manifest.json" => "service_worker#manifest"
# Other routes...
end
2. Create the Controller for the Service Worker:
In app/controllers/service_worker_controller.rb, you’ll need to add logic to serve the service worker and manifest files:
class ServiceWorkerController < ApplicationController
protect_from_forgery except: :service_worker
def service_worker
respond_to do |format|
format.js { render file: 'service_worker/service_worker.js' }
end
end
def manifest
render json: {
name: "Your App",
short_name: "App",
display: "standalone",
start_url: "/",
theme_color: "#000000",
background_color: "#FFFFFF"
}
end
end
3. Create the Service Worker File:
Create a directory app/views/service_worker/ and add a service_worker.js.erb file. This file will contain your service worker logic.
Create a directory app/views/service_worker/ and add a service_worker.js.erb file. This file will contain your service worker logic.
Step 2: Implement the Service Worker
Now that the basic setup is ready, let’s implement the service worker to cache important resources such as CSS, JavaScript, images, and the /manifest.json file.
1. Define Caches and Install Event:
In your service_worker.js.erb, define the caches and handle the installation process:
const version = '1.0.0'; // Update the version with each new deployment
const CACHE_NAME = `offline-cache-${version}`;
const URLS_TO_CACHE = [
'/manifest.json',
'/offline.html',
'<%= asset_path("application.css") %>',
'<%= asset_path("application.js") %>',
'https://unpkg.com/dexie/dist/dexie.js',
'https://fonts.googleapis.com/css?family=Cousine:400,400i,700,700i',
// Add any other assets you want to cache
];
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open(CACHE_NAME)
.then(function(cache) {
console.log('Opened cache');
return cache.addAll(URLS_TO_CACHE);
})
);
});
2. Handling Fetch Events:
The service worker needs to intercept fetch events and return cached responses when offline. Add the following logic to handle fetch requests:
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request)
.then(function(response) {
return response || fetch(event.request).catch(() => {
if (event.request.mode === 'navigate') {
return caches.match('/offline.html');
}
});
})
);
});
3. Activate Event:
During the activation of a new service worker version, it’s essential to delete old caches that are no longer in use. Here’s the code to handle the activation event:
self.addEventListener('activate', function(event) {
const currentCaches = [CACHE_NAME];
event.waitUntil(
caches.keys().then(function(cacheNames) {
return Promise.all(
cacheNames.map(function(cache) {
if (!currentCaches.includes(cache)) {
console.log('Deleting old cache:', cache);
return caches.delete(cache);
}
})
);
})
);
return self.clients.claim();
});
Step 3: Adding the Manifest and Offline Fallback Page
To provide offline support, we need an offline fallback page that will be displayed when the user loses connection and navigates to a page that isn’t cached.
1. Create the Offline Page:
Add an offline.html file in the public directory. This page will be displayed when the service worker can’t fetch a network resource:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Offline</title>
<style>
body { font-family: Arial, sans-serif; text-align: center; padding: 50px; }
</style>
</head>
<body>
<h1>You're Offline</h1>
<p>Please check your internet connection and try again.</p>
</body>
</html>
2. Add the Manifest JSON:
Add the manifest file in the public directory as well. This file allows your app to be added to the home screen on mobile devices and sets theme colors:
{
"name": "My Awesome App",
"short_name": "App",
"display": "standalone",
"start_url": "/",
"theme_color": "#000000",
"background_color": "#FFFFFF"
}
Step 4: Testing Your Offline-Ready App
Once your service worker is set up and installed, you can test your app’s offline functionality using the following steps:
1. Use Chrome Developer Tools:
- Open your app in Google Chrome.
- Open DevTools (F12 or Cmd+Opt+I).
- Go to the Application tab.
- Under Service Workers, ensure the service worker is registered.
- In the Cache Storage section, check that your assets are cached.
2. Simulate Offline Mode:
- In DevTools, go to the Network tab.
- Use the Offline setting to simulate being disconnected from the internet.
- Navigate through your app to see which assets are loaded from the cache and ensure your offline page works as expected.
Conclusion
Adding a service worker to your Rails 7 app is a powerful way to enhance the user experience by providing offline support. By caching essential resources and handling network failures gracefully, you ensure that your users can still interact with your app even when they're not connected to the internet.
With just a few configurations and additions, your Rails app can now function reliably in offline conditions. Happy coding!
With just a few configurations and additions, your Rails app can now function reliably in offline conditions. Happy coding!
Credits and Attribution
This post was inspired by Alicia Paz’s guide on making a Rails app work offline, which provided valuable insights into Progressive Web App (PWA) setup and service worker integration. If you're interested in a deeper dive into PWA concepts for Rails apps, be sure to check out her post!