Skip to content

Commit 054fdcb

Browse files
Shen-wbJustin-dynamsoftKeillion
authored
PWA caching strategy; iOS pwa fault tolerance
* update browser compatibility versions in README.md * PWA caching strategy; iOS pwa fault tolerance; * cache cros script * rep clone before used * Update README.md --------- Co-authored-by: Justin <Justin@dynamsoft.com> Co-authored-by: Keillion <keillionv@gmail.com>
1 parent ae40600 commit 054fdcb

4 files changed

Lines changed: 239 additions & 188 deletions

File tree

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ The following table is a list of supported browsers based on the above requireme
2121

2222
| Browser Name | Version |
2323
| :----------: | :--------------: |
24-
| Chrome | v88+ |
25-
| Firefox | v89+ |
26-
| Edge | v88+ |
27-
| Safari | v15+ |
24+
| Chrome | v78+ |
25+
| Firefox | v68+ |
26+
| Edge | v79+ |
27+
| Safari | v14.5+ |
2828

2929
Apart from the browsers, the operating systems may impose some limitations of their own that could restrict the use of the SDK.
3030

frameworks/pwa/README.md

Lines changed: 175 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -22,91 +22,107 @@ We will try to turn our basic "Hello World" sample into a PWA. Follow these step
2222

2323
```html
2424
<!-- /helloworld-pwa.html -->
25-
<!DOCTYPE html>
25+
<!DOCTYPE html>
2626
<html lang="en">
27-
<head>
28-
<meta charset="utf-8" />
29-
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
30-
<title>Dynamsoft Barcode Reader PWA Sample - Hello World</title>
31-
</head>
32-
33-
<body>
34-
<h1>Hello World for PWA</h1>
35-
<div id="camera-view-container" style="width: 100%; height: 80vh"></div>
36-
<br />
37-
Results:
38-
<div id="results" style="width: 100%; height: 10vh; overflow: auto"></div>
39-
<script src="https://cdn.jsdelivr.net/npm/dynamsoft-barcode-reader-bundle@11.4.2001/dist/dbr.bundle.js"></script>
40-
<script>
41-
if (location.protocol === "file:") {
42-
const message = `The page is opened via file:// and our SDKs may not work properly. Please open the page via https:// or host it on "http://localhost/".`;
43-
console.warn(message);
44-
alert(message);
45-
}
46-
</script>
47-
<script>
48-
/** LICENSE ALERT - README
49-
* To use the library, you need to first specify a license key using the API "initLicense()" as shown below.
50-
*/
51-
52-
Dynamsoft.License.LicenseManager.initLicense("DLS2eyJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSJ9");
53-
54-
/**
55-
* You can visit https://www.dynamsoft.com/customer/license/trialLicense?utm_source=samples&product=dbr&package=js to get your own trial license good for 30 days.
56-
* Note that if you downloaded this sample from Dynamsoft while logged in, the above license key may already be your own 30-day trial license.
57-
* For more information, see https://www.dynamsoft.com/barcode-reader/docs/web/programming/javascript/user-guide/index.html?ver=11.4.2001&cVer=true#specify-the-license&utm_source=samples or contact support@dynamsoft.com.
58-
* LICENSE ALERT - THE END
59-
*/
60-
61-
// Defined globally for easy debugging.
62-
let cameraEnhancer, cvRouter;
63-
64-
(async function () {
65-
try {
66-
// Create a `CameraEnhancer` instance for camera control and a `CameraView` instance for UI control.
67-
const cameraView = await Dynamsoft.DCE.CameraView.createInstance();
68-
cameraEnhancer = await Dynamsoft.DCE.CameraEnhancer.createInstance(cameraView);
69-
document.querySelector("#camera-view-container").append(cameraView.getUIElement()); // Get default UI and append it to DOM.
70-
71-
// Create a `CaptureVisionRouter` instance and set `CameraEnhancer` instance as its image source.
72-
cvRouter = await Dynamsoft.CVR.CaptureVisionRouter.createInstance();
73-
cvRouter.setInput(cameraEnhancer);
74-
75-
// Define a callback for results.
76-
const resultReceiver = new Dynamsoft.CVR.CapturedResultReceiver();
77-
resultReceiver.onDecodedBarcodesReceived = (result) => {
27+
28+
<head>
29+
<meta charset="utf-8" />
30+
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
31+
<meta name="description" content="Read barcodes from camera with Dynamsoft Barcode Reader in a PWA application." />
32+
<meta name="keywords" content="barcode, camera, PWA" />
33+
<title>Dynamsoft Barcode Reader PWA Sample - Hello World (Decode via Camera)</title>
34+
</head>
35+
36+
<body>
37+
<h1>Hello World for PWA</h1>
38+
<div id="camera-view-container" style="width: 100%; height: 80vh"></div>
39+
<br />
40+
Results:
41+
<div id="results" style="width: 100%; height: 10vh; overflow: auto"></div>
42+
<!-- crossorigin="anonymous" is required for service worker caching. -->
43+
<script crossorigin="anonymous" src="https://cdn.jsdelivr.net/npm/dynamsoft-barcode-reader-bundle@11.4.2001/dist/dbr.bundle.js"></script>
44+
<script>
45+
/** LICENSE ALERT - README
46+
* To use the library, you need to first specify a license key using the API "initLicense()" as shown below.
47+
*/
48+
49+
Dynamsoft.License.LicenseManager.initLicense("DLS2eyJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSJ9");
50+
51+
/**
52+
* You can visit https://www.dynamsoft.com/customer/license/trialLicense?utm_source=samples&product=dbr&package=js to get your own trial license good for 30 days.
53+
* Note that if you downloaded this sample from Dynamsoft while logged in, the above license key may already be your own 30-day trial license.
54+
* For more information, see https://www.dynamsoft.com/barcode-reader/docs/web/programming/javascript/user-guide/index.html?ver=11.4.2001&cVer=true#specify-the-license&utm_source=samples or contact support@dynamsoft.com.
55+
* LICENSE ALERT - THE END
56+
*/
57+
58+
// Optional. Used to load wasm resources in advance, reducing latency between video playing and barcode decoding.
59+
Dynamsoft.Core.CoreModule.loadWasm();
60+
61+
// Defined globally for easy debugging.
62+
let cameraEnhancer, cvRouter;
63+
64+
(async () => {
65+
try {
66+
// Create a `CameraEnhancer` instance for camera control and a `CameraView` instance for UI control.
67+
const cameraView = await Dynamsoft.DCE.CameraView.createInstance();
68+
cameraEnhancer = await Dynamsoft.DCE.CameraEnhancer.createInstance(cameraView);
69+
// Get default UI and append it to DOM.
70+
document.querySelector("#camera-view-container").append(cameraView.getUIElement());
71+
72+
// Hide the "Powered by Message" overlay on the scanner view
73+
// cameraView.setPowerByMessageVisible(false);
74+
75+
// Create a `CaptureVisionRouter` instance and set `CameraEnhancer` instance as its image source.
76+
cvRouter = await Dynamsoft.CVR.CaptureVisionRouter.createInstance();
77+
cvRouter.setInput(cameraEnhancer);
78+
79+
// Define a callback for results.
80+
await cvRouter.addResultReceiver({
81+
onDecodedBarcodesReceived: (result) => {
82+
if (!result.barcodeResultItems.length) return;
83+
84+
const resultsContainer = document.querySelector("#results");
85+
resultsContainer.textContent = "";
86+
console.log(result);
7887
for (let item of result.barcodeResultItems) {
79-
if (!result.barcodeResultItems.length) return;
80-
81-
const resultsContainer = document.querySelector("#results");
82-
resultsContainer.textContent = "";
83-
console.log(result);
84-
for (let item of result.barcodeResultItems) {
85-
resultsContainer.textContent += `${item.formatString}: ${item.text}\n\n`;
86-
}
88+
resultsContainer.textContent += `${item.formatString}: ${item.text}\n\n`;
8789
}
88-
};
89-
cvRouter.addResultReceiver(resultReceiver);
90-
91-
// Filter out unchecked and duplicate results.
92-
const filter = new Dynamsoft.Utility.MultiFrameResultCrossFilter();
93-
// Filter out unchecked barcodes.
94-
filter.enableResultCrossVerification("barcode", true);
95-
// Filter out duplicate barcodes within 3 seconds.
96-
filter.enableResultDeduplication("barcode", true);
97-
await cvRouter.addResultFilter(filter);
98-
99-
// Open camera and start scanning single barcode.
100-
await cameraEnhancer.open();
101-
await cvRouter.startCapturing("ReadSingleBarcode");
102-
} catch (ex) {
103-
let errMsg = ex.message || ex;
104-
console.error(ex);
105-
alert(errMsg);
90+
},
91+
});
92+
93+
// Filter out unchecked and duplicate results.
94+
const filter = new Dynamsoft.Utility.MultiFrameResultCrossFilter();
95+
// Filter out unchecked barcodes.
96+
filter.enableResultCrossVerification("barcode", true);
97+
// Filter out duplicate barcodes within 3 seconds.
98+
filter.enableResultDeduplication("barcode", true);
99+
await cvRouter.addResultFilter(filter);
100+
101+
// Open camera and start scanning barcode.
102+
await cameraEnhancer.open();
103+
104+
if('disabled' === cameraEnhancer.singleFrameMode){
105+
// Video played successfully.
106+
cameraView.setScanLaserVisible(true);
107+
}else{
108+
// In iOS PWA, if camera open failed,
109+
// failback to `singleFrameMode = 'camera'`.
110+
// In iOS PWA wasm is slow, so we increase the timeout.
111+
const cvrSettings = await cvRouter.outputSettings('ReadBarcodes_SpeedFirst');
112+
cvrSettings.CaptureVisionTemplates[0].Timeout = 15000;
113+
await cvRouter.initSettings(cvrSettings);
106114
}
107-
})();
108-
</script>
109-
</body>
115+
116+
await cvRouter.startCapturing("ReadBarcodes_SpeedFirst");
117+
} catch (ex) {
118+
let errMsg = ex.message || ex;
119+
console.error(ex);
120+
alert(errMsg);
121+
}
122+
})();
123+
</script>
124+
</body>
125+
110126
</html>
111127
```
112128

@@ -133,44 +149,68 @@ if ('serviceWorker' in navigator) {
133149
* Next, create the `service-worker.js` file with the following content:
134150

135151
```javascript
136-
/* /service-worker.js */
137152
// Files to cache
138-
const cacheName = 'helloworld-pwa';
139-
const appShellFiles = [
140-
'./helloworld-pwa.html',
153+
const CACHE_PREFIX = "helloworld-pwa-";
154+
const CACHE_NAME = `${CACHE_PREFIX}v1`;
155+
// Here we choose some files into ASSETS_TO_CACHE to cache.
156+
// You can find these files in "node_modules/dynamsoft-barcode-reader-bundle/dist".
157+
const ASSETS_TO_CACHE = [
158+
"./helloworld-pwa.html",
141159
];
142160

143161
// Installing Service Worker
144162
self.addEventListener("install", (e) => {
145163
console.log("[Service Worker] Install");
146164
e.waitUntil(
147-
(async () => {
148-
const cache = await caches.open(cacheName);
165+
caches.open(CACHE_NAME).then((cache) => {
149166
console.log("[Service Worker] Caching all: app shell and content");
150-
await cache.addAll(appShellFiles);
151-
})()
167+
return cache.addAll(ASSETS_TO_CACHE);
168+
})
169+
);
170+
// Force the waiting service worker to become the active one
171+
self.skipWaiting();
172+
});
173+
174+
// 2. Activate: Clean up old caches
175+
self.addEventListener('activate', (e) => {
176+
e.waitUntil(
177+
caches.keys().then((cacheNames) => {
178+
return Promise.all(
179+
cacheNames.map((cacheName) => {
180+
if (cacheName.startsWith(CACHE_PREFIX) && cacheName !== CACHE_NAME) {
181+
console.log('Deleting old cache:', cacheName);
182+
return caches.delete(cacheName);
183+
}
184+
})
185+
);
186+
})
152187
);
153188
});
154189

155-
self.addEventListener("fetch", (e) => {
190+
// 3. Fetch: Stale-While-Revalidate Strategy
191+
self.addEventListener('fetch', (e) => {
156192
e.respondWith(
157-
(async () => {
158-
// Fetch cached response if exists
159-
const cachedResponse = await caches.match(e.request);
160-
console.log(`[Service Worker] Fetching resource: ${e.request.url}`);
161-
if (cachedResponse) {
162-
return cachedResponse;
163-
}
193+
caches.match(e.request).then((cachedResponse) => {
194+
// Return cached response if found, but fetch a fresh version anyway
195+
const fetchPromise = fetch(e.request).then((networkResponse) => {
196+
if(
197+
'GET' === e.request.method && networkResponse.ok &&
198+
// Authorization requests should not be cached
199+
!/https:\/\/.*?\.dynamsoft.com\/auth/.test(e.request.url)
200+
// You can add other filter conditions
201+
){
202+
// Update the cache with the new version
203+
const clonedRep = networkResponse.clone();
204+
caches.open(CACHE_NAME).then((cache) => {
205+
cache.put(e.request, clonedRep);
206+
console.log(`[Service Worker] Cache updated: ${e.request.url}`);
207+
});
208+
}
209+
return networkResponse;
210+
});
164211

165-
// Otherwise, fetch from network
166-
const response = await fetch(e.request);
167-
const cache = await caches.open(cacheName);
168-
console.log(`[Service Worker] Caching new resource: ${e.request.url}`);
169-
if (e.request.method !== "POST") {
170-
cache.put(e.request, response.clone());
171-
}
172-
return response;
173-
})()
212+
return cachedResponse || fetchPromise;
213+
})
174214
);
175215
});
176216
```
@@ -179,15 +219,6 @@ With the above code, the application can now work offline because the service wo
179219

180220
For more information, refer to [Making PWAs work offline with Service workers](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Offline_Service_workers).
181221

182-
> Note:
183-
>
184-
> Since the files are cached, changes we make in later steps may not be reflected immediately. To ensure updates are applied, clear the cache after changes are made. You can do this by running the following code in the browser console:
185-
>
186-
> ```javascript
187-
> const cacheName = 'helloworld-pwa';
188-
> const cache = await caches.delete(cacheName);
189-
> ```
190-
191222
### Use a web manifest file to make the application installable
192223

193224
A web manifest file contains a list of information about a website in JSON format. This information is used to enable the web app to be installed on a device.
@@ -261,38 +292,43 @@ const engineResourcePaths = {
261292
};
262293

263294
// Files to cache
264-
const cacheName = "helloworld-pwa";
265-
const appShellFiles = [
295+
const CACHE_PREFIX = "helloworld-pwa-";
296+
const CACHE_NAME = `${CACHE_PREFIX}v1`;
297+
// Here we choose some files into ASSETS_TO_CACHE to cache.
298+
// You can find these files in "node_modules/dynamsoft-barcode-reader-bundle/dist".
299+
const ASSETS_TO_CACHE = [
266300
"./helloworld-pwa.html",
267301
"./dynamsoft-192x192.png",
268302
"./dynamsoft-512x512.png",
269303
"./helloworld-pwa.json",
304+
`${engineResourcePaths.dbrBundle}dbr.bundle.js`,
270305
`${engineResourcePaths.dbrBundle}dbr.bundle.worker.js`,
271-
`${engineResourcePaths.dbrBundle}dynamsoft-capture-vision-bundle.js`
272-
`${engineResourcePaths.dbrBundle}dynamsoft-capture-vision-bundle.wasm`,
273-
`${engineResourcePaths.dbrBundle}models/OneDDeblur.data`,
274-
`${engineResourcePaths.dbrBundle}parser-resources/AADHAAR_Map.txt`,
275-
`${engineResourcePaths.dbrBundle}parser-resources/AADHAAR.dcpres`,
276-
`${engineResourcePaths.dbrBundle}parser-resources/AAMVA_DL_ID_WITH_MAG_STRIPE.dcpres`,
277-
`${engineResourcePaths.dbrBundle}parser-resources/AAMVA_DL_ID.dcpres`,
278-
`${engineResourcePaths.dbrBundle}parser-resources/AAMVA_Map.txt`,
279-
`${engineResourcePaths.dbrBundle}parser-resources/GS1_AI_Map.txt`,
280-
`${engineResourcePaths.dbrBundle}parser-resources/GS1_AI.txt`,
281-
`${engineResourcePaths.dbrBundle}parser-resources/MRTD_Map.txt`,
282-
`${engineResourcePaths.dbrBundle}parser-resources/MRTD_TD1_ID.dcpres`,
283-
`${engineResourcePaths.dbrBundle}parser-resources/MRTD_TD2_FRENCH_ID.dcpres`,
284-
`${engineResourcePaths.dbrBundle}parser-resources/MRTD_TD2_ID.dcpres`,
285-
`${engineResourcePaths.dbrBundle}parser-resources/MRTD_TD2_VISA.dcpres`,
286-
`${engineResourcePaths.dbrBundle}parser-resources/MRTD_TD3_PASSPORT.dcpres`,
287-
`${engineResourcePaths.dbrBundle}parser-resources/MRTD_TD3_VISA.dcpres`,
288-
`${engineResourcePaths.dbrBundle}parser-resources/SOUTH_AFRICA_DL_Map.txt`,
289-
`${engineResourcePaths.dbrBundle}parser-resources/SOUTH_AFRICA_DL.dcpres`,
290-
`${engineResourcePaths.dbrBundle}parser-resources/VIN_Map.txt`,
291-
`${engineResourcePaths.dbrBundle}parser-resources/VIN.dcpres`,
306+
`${engineResourcePaths.dbrBundle}dynamsoft-barcode-reader-bundle-ml-simd.js`,
307+
`${engineResourcePaths.dbrBundle}dynamsoft-barcode-reader-bundle-ml-simd.wasm`,
308+
`${engineResourcePaths.dbrBundle}models/Code128Decoder.data`,
309+
`${engineResourcePaths.dbrBundle}models/EAN13Decoder.data`,
310+
`${engineResourcePaths.dbrBundle}models/Code39ITFDecoder.data`,
292311
`${engineResourcePaths.dbrBundle}templates/DBR-PresetTemplates.json`,
293-
`${engineResourcePaths.dbrBundle}ui/barcode-scanner.ui.xml`,
294312
`${engineResourcePaths.dbrBundle}ui/dce.ui.xml`,
295313
`${engineResourcePaths.dbrBundle}ui/dls.license.dialog.html`,
314+
315+
// `${engineResourcePaths.dbrBundle}dynamsoft-barcode-reader-bundle.js`,
316+
// `${engineResourcePaths.dbrBundle}dynamsoft-barcode-reader-bundle.wasm`,
317+
// `${engineResourcePaths.dbrBundle}dynamsoft-barcode-reader-bundle-ml-simd-pthread.js`,
318+
// `${engineResourcePaths.dbrBundle}dynamsoft-barcode-reader-bundle-ml-simd-pthread.worker.js`,
319+
// `${engineResourcePaths.dbrBundle}dynamsoft-barcode-reader-bundle-ml-simd-pthread.wasm`,
320+
// `${engineResourcePaths.dbrBundle}models/OneDDeblur.data`,
321+
// `${engineResourcePaths.dbrBundle}models/OneDLocalization.data`,
322+
// `${engineResourcePaths.dbrBundle}models/DataMatrixQRCodeLocalization.data`,
323+
// `${engineResourcePaths.dbrBundle}models/DataMatrixQRCodeDeblur.data`,
324+
// `${engineResourcePaths.dbrBundle}models/PDF417Deblur.data`,
325+
// `${engineResourcePaths.dbrBundle}models/PDF417Localization.data`,
326+
// `${engineResourcePaths.dbrBundle}parser-resources/AADHAAR.data`,
327+
// `${engineResourcePaths.dbrBundle}parser-resources/AAMVA_DL_ID.data`,
328+
// `${engineResourcePaths.dbrBundle}parser-resources/GS1_AI.data`,
329+
// `${engineResourcePaths.dbrBundle}parser-resources/MRTD.data`,
330+
// `${engineResourcePaths.dbrBundle}parser-resources/SOUTH_AFRICA_DL.data`,
331+
// `${engineResourcePaths.dbrBundle}parser-resources/VIN.data`,
296332
];
297333
```
298334

0 commit comments

Comments
 (0)