Offline Sync

Built-in barcode reader and elegant icons elevate your online and offline apps.

AJAX(112) App Studio(7) Apple(1) Application Builder(245) Application Factory(207) ASP.NET(95) ASP.NET 3.5(45) ASP.NET Code Generator(72) ASP.NET Membership(28) Azure(18) Barcode(2) Barcodes(3) BLOB(18) Business Rules(1) Business Rules/Logic(140) BYOD(13) Caching(2) Calendar(5) Charts(29) Cloud(14) Cloud On Time(2) Cloud On Time for Windows 7(2) Code Generator(54) Collaboration(11) command line(1) Conflict Detection(1) Content Management System(12) COT Tools for Excel(26) CRUD(1) Custom Actions(1) Data Aquarium Framework(122) Data Sheet(9) Data Sources(22) Database Lookups(50) Deployment(22) Designer(177) Device(1) DotNetNuke(12) EASE(20) Email(6) Features(101) Firebird(1) Form Builder(14) Globalization and Localization(6) How To(1) Hypermedia(2) Inline Editing(1) Installation(5) JavaScript(20) Kiosk(1) Low Code(3) Mac(1) Many-To-Many(4) Maps(6) Master/Detail(36) Microservices(4) Mobile(63) Mode Builder(3) Model Builder(3) MySQL(10) Native Apps(5) News(18) OAuth(9) OAuth Scopes(1) OAuth2(13) Offline(20) Offline Apps(4) Offline Sync(5) Oracle(11) PKCE(2) Postgre SQL(1) PostgreSQL(2) PWA(2) QR codes(2) Rapid Application Development(5) Reading Pane(2) Release Notes(183) Reports(48) REST(29) RESTful(29) RESTful Workshop(15) RFID tags(1) SaaS(7) Security(81) SharePoint(12) SPA(6) SQL Anywhere(3) SQL Server(26) SSO(1) Stored Procedure(4) Teamwork(15) Tips and Tricks(87) Tools for Excel(2) Touch UI(93) Transactions(5) Tutorials(183) Universal Windows Platform(3) User Interface(338) Video Tutorial(37) Web 2.0(100) Web App Generator(101) Web Application Generator(607) Web Form Builder(40) Web.Config(9) Workflow(28)
Offline Sync
Sunday, March 5, 2023PrintSubscribe
Barcodes with Camera and Material Icons 2.0

Code On Time release introduces the new built-in Barcode Reader and enhances Touch UI with the Material Icons 2.0. Other highlights include extended support for apps deployed to a virtual folder in the RESTful API and Offline Sync.

Barcode Reader

The built-in barcode reader uses the device camera to read 1-D and 2-D barcodes. The reader viewfinder can be displayed inline next to the designated fields to allow easy capture of barcode values. The viewfinder is not visible if the field has a value. The built-in Scan form allows scanning barcodes with the optional inventory mode at any time. The UI Automation rules allow complex custom processing sequences in response to the scanned barcodes.

Integrated Barcode Reader makes it easy to scan barcodes into the fields. Custom automation rules are triggered from the standalone Scan form when barcodes are detected and sent for processing.

This is an example of the inline barcode reader viewfinder.

The Barcode and Serial Number data fields are tagged as input-scanner in Products / views / createForm1 view. The barcode reader viewfinder is displayed below the Serial Number when the Barcode value is detected or entered directly by the user. If all tagged fields have a value, then the barcode reader will remain inactive until one of the values is cleared.

The standard Scan form is shown next. The form is presented in the fullscreen mode on mobile phones with a small screen. Users have access to the Scan action in the app context menu.

Users can enter the hard-to-scan barcodes in the input above the viewfinder. The Inventory Mode toggle allows collecting multiple barcodes. The Clear button will erase all barcodes in the inventory. The Undo button will remove the last detected barcode from the inventory. The Copy icon displayed on the right side of the detected barcodes will copy the entire set to the clipboard. The detected barcodes are sent instantly for processing if the Inventory Mode is not enabled.

Material Icons 2.0

A huge collection of Google Material Icons is now available to developers. Enhance your apps with more than 2,500 icons with the consistent design and exceptional rendering quality on any screen. For example, you can customize the built-in barcode reader with the following images:

Make sure to enter the icon name in lowercase with the material-icon- prefix and replace spaces with the dashes. For example, enter material-icon-qr-code to specify the “Qr Code” icon for a custom action.

Bug Fixes and Enhancements

The following bug fixes and enhancements are also included in the release:

  • (Barcode Reader) The built-in barcode reader will display inline below the field without a value when the corresponding data field is marked with the input-scanner tag.
  • (Barcode Reader) The system action Scan is available in the interactive context menu. It will bring up the Scan form that allows scanning 1-D and 2-D barcodes.
  • (RTF) The Rich Text Editor toolbar will appear below the input if the "top" positioning will hide it below the page header or status bar.
  • (UI Automation) Failed to run automation will exit only if there is no pending "if" test.
  • (UI Automation) Fixed implementation of and app.dec to use the identifier option when calling the app.val method.
  • (Touch UI) The "clear" icon is aligned vertically next to the filter text in the view header.
  • (Touch UI) Method $app.touch.uiAutomation returns 'true' if an automation is in progress.
  • (Barcode Reader) Inline viewfinder is not displayed while the app is executing an automation.
  • (UI Automation) Asynchronous UI automation rule step will show an error and stop automation execution when an error is thrown.
  • (UI Automation) The rejected “if” chain will continue execution of automation if an asynchronous method was rejected without error.
  • (Offline Sync) The theme option is not available in the Settings menu on the offline pages when the app is offline.
  • (Touch UI) The "copy" action is centered vertically in the field with text-action-copy tag.
  • (Touch UI) The default display density on high-definition screens is now set to Compact value. Previously the small high-definition displays activated the Comfortable density. The new standard density is simplifying the upcoming Live Project Designer.
  • (Touch UI) The default density on the low-resolution displays is Condensed.
  • (Touch UI) Latest material icons are included.
  • (Framework) Replace the direct references to the "AppState" item in the context with the reference to the static ApplicationServices.AppState property. It returns the JObject instance representing the application state data when the user must perform a system action such as approving access to the account data for the external app. The user interface is adjusted to provide the minimal features until the application state is cleared. App pages are returned without content.
  • (RESTful) Hypermedia URLs are constructed correctly for apps deployed to the virtual path.
  • (RESTful) Embedding works correctly for the apps deployed to the virtual path.
  • (RESTful) OAuth 2.0 implementation uses the ‘'~/oauth2/v2' endpoint when interacting with the app while users allow or deny access for an external app. This ensures that the protocol will work in the apps deployed to the virtual folders.
  • (RESTful) Method $app.restful uses the serverUrl in the standalone apps to complete the hypermedia links. This enables correct handling of hypermedia produced by apps deployed to a virtual folder.
  • (Data Aquarium) Method $app.cookie removes the trailing '/' when creating cookies in the apps deployed to the virtual folder.
  • (Touch UI) The initialization of UI is performed sooner when the DOM is ready. Previously the UI was initialized after a timeout.
  • (Framework) Pages with data-ui-framework="none" attribute do not link the app.all.min.js script. The minimal set of required JavaScript files is linked instead.
  • (Framework) Serving of addon js/css is now compatible with apps running on the virtual folder.
  • (Offline Sync) An exception raised when synching data will hide the progress screen and show the error message.
  • (Offline Sync) New version is compatible with the enhanced uploading of BLOBs that allows synching with the apps deployed to a virtual folder.
  • (Offline Data Processor) ODP raises the event on the document object. The event is provided with the odp, controller, and view properties. Developers can handle the event by assigning the value to the filter property of the event instance. This allows creating client-side filters for the views. For example, the following filter will limit the list of suppliers to those located in the United States.
  • (Touch UI) The Search API does not try to load a custom search survey. Both 'Quick Find" and "Advanced Search" appear faster when activated for the first time on the page.
  • (App Gen) Microsoft IIS Express prerequisite check is performed when the UI is about to be shown. Microsoft IIS Express installation prompt is automatically displayed if the software is not installed.
  • (App Gen) MIcrosoft IIS Express location is probed in the machine-specific "Program Files" folder.
  • (Data Aquarium) The event is raised on the document object when the data is received by a DataView instance. Custom apps can customize the client-side properties of the response to alter presentation before it is processed on the client. Event object properties dataView and response provide access to the context of the event. Developers can assign an instance of the Promise object to the event object promise property if the additional information needs to be acquired asynchronously from the external source before the DataView instance is allowed to process the 'response'
  • (Data Aquarium) Fixed the incorrect loading of "also" resources when $app.getScript is called.
  • (Touch UI) Tag the lookup field or DataView field as view-selector-remember-none to force the app to forget the last view that was selected by the user on the page. The first view will be selected when the same form is displayed.
  • (Touch UI) Tag the lookup field or DataView field as view-style-remember-none' to force the app to forget the last view style that was activated by the user on the page. The default view will be selected when the same form is displayed.
  • (Framework) Class AppResourceManager will suppress exceptions when trying to read a non-existing resource if the app's namespace is FreeTrial. This allows creating apps without membership with the Free Trial of Code On Time. Previously an exception was raised.
  • (Framework) Added the missing "Actions" enumerator value for the 'style' attribute of lookups to the DataAquarium.xsd.
Tuesday, January 31, 2023PrintSubscribe
Welcome to the Offline World!

Code On Time release and Offline Sync 2.0 are bringing about the era of “always offline” applications!

An app created with Code On Time can work both in the online and in the offline modes. The latter requires the Offline Sync added to the app configuration. Users with the "Offline" role will have the app front-end and data downloaded into the WebView storage upon sign in. Offline users must explicitly sync the pending changes with the application on the server. Offline Sync works in the browsers and in the installed apps.
An app created with Code On Time can work both in the online and in the offline modes. The latter requires the Offline Sync added to the app configuration. Users with the "Offline" role will have the app front-end and data downloaded into the WebView storage upon sign in. Offline users must explicitly sync the pending changes with the application on the server. Offline Sync works in the browsers and in the installed apps.

Offline Apps with “Online-First” Approach

You may be asking yourself why one needs the offline mode when everybody and everything is connected nowadays! Professionals working in the field will tell you otherwise. There are isolated places even in the middle of a modern megapolis. Never count on the internet connection to be available if you are on the go. This presents a unique challenge for application developers. Apps designed for the mobile workforce must work in the “always offline” mode to be effective. That typically means that at least three applications will need to be developed simultaneously:

  • Mobile front-end working in the offline mode.
  • Online back-end running on a server to allow data synchronization with the application database.
  • Online front-end working in browsers to provide authentication and to service the needs of online users.
Code On Time introduces the “online first” approach to the offline application development.

Start with the online application featuring the powerful back-end and the embedded online front-end that works in any modern browser.

Next, mark the data-aware pages as “offline” to enable the atomic data entry in application forms. Forms with complex data dependencies turn into electronic documents on the application pages marked as “offline”. All related items are either committed to the database together or discarded at once. The root of the “electronic document” is the top-level form.

Atomic Data Entry benefits an online app. Users can enter details along with the new master record. Forms with complex data relationships are saved or canceled as a single electronic document. Data is persisted in the context of a database transaction.

Finally, enable the Offline Sync Add-On the application configuration. The “Offline” role is assigned to the user accounts of the field workers. The magic starts when users are signing-in. Application front-end and user account data are downloaded to the device and users remain offline from there on. Data entries are applied to the local data items and logged. Offline users will have to synchronize the pending changes and refresh the app front-end and data periodically.

Offline mode is possible in all modern browsers implementing the ServiceWorker, IndexedDB, and Cache APIs. The front-end of the application can be installed to run natively on a device.

Blob Thumbnail Refreshing

This product update introduces the new method of the thumbnail refreshing when the corresponding BLOBs are changed.

The thumbnails used to be cached on the client for up to 5 minutes. This dramatically improves the performance of an app. It also creates a problem when the BLOBs are changed - the thumbnail will remain the same until the expiration of the caching instruction. One good solution is to include the hash value of the BLOB data in the URL of a thumbnail, but that would require either processing a potentially large amount of data on the fly or preserving the pre-calculated hash value in the database. The problem has become even more pronounced when we have introduced the Drawing Pad and the offline mode. Users working in the field are taking photos and marking them up a lot! Thumbnails must reflect the state of the photo to avoid the confusion!

Now the front-end keeps track of the BLOBs changed or replaced by the user. The timestamp is added to the BLOB URLs to ensure that the fresh thumbnails are presented to the person making changes. Other users will see the fresh thumbnails every 10 minutes.

Marked up images will have a fresh thumbnail displayed to the user making the changes. Other application users will see the fresh thumbnails every ten minutes.
Marked up images will have a fresh thumbnail displayed to the user making the changes. Other application users will see the fresh thumbnails every ten minutes.

Coming Next

Take a moment to learn about the upcoming features or jump straight to the list of bug fixes and enhancements included in the release.

Barcode Scanner Form and Inline Barcode Scanner Window

The goal of the next “feature” release is to make it possible to scan barcodes and QR codes with the device camera. The standard Scan action will be available in the system context menu of apps with Touch UI. It will activate the Scanner Form and trigger the barcode processing sequences when there is a positive “read”. The “scan” icon will be permanently available in the Kiosk UI.

We will also provide a mechanism to display the inline scanner window next to the inputs in the forms. The act of scanning will enter the value in the input. Mark the field as Causes Calculate to handle the event of scanning with a business rule.

Live Project Designer v9

The remainder of the year will be dedicated to the new Live Project Designer v9. We do want to make it as simple as possible to build apps with Code On Time. The new project designer will become the primary development tool embedded directly in the live applications.

Here is the list of the existing product features that will be used to put it together:

  • Development Mode is the standard feature that can be activated in the live apps running on the localhost. Choose the Developer Tools option in the context menu to see it live. This mode is shown in the screenshot below.
  • Property Window is already shipping in the code base of apps created with Code On Time. This is the survey driven by a JavaScript object describing the available properties. See the screenshot below.
  • App Studio is the application launched every time you click on the Personal Activation Code in the top right corner of the start page in the Code On Time app builder. This app has been created with Code On Time and runs on IIS Express just like your own apps do. Today its purpose is to redirect users to The portal talks to App Studio to activate the code generation library.
  • Progressive Web Applications spec implementation allows apps to be installable and provides them with the sandboxed Service Worker that is always up-and-running.
  • Model Builder is the JavaScript component running in a WebView embedded in the Windows form.
  • RESTful API Engine is the standard component of apps created with Code On Time providing limitless integration capabilities.
  • Content Hub is our proprietary software that implements a powerful content management system based on documents stored in Google Drive, Microsoft Office 365, or file system. This post was created with Content Hub from a document in Google Drive.
  • Command Line Mode of code generation.

Here is how the App Studio will change the development process:

  • Code On Time will minimize itself into the Windows icon tray when started.
  • App Studio will be launched in the default web browser and present a list of projects you are working on.
  • Users will select an app and the App Studio will redirect them to the live application instance running locally in the Development Mode. The app will be sized to a specific device. The screenshot below shows the iPad device simulation.
  • Users will create new projects by entering a few basic properties (Name, programming language, etc). App Studio will instruct Code On Time to generate the app code in the command line mode and redirect the user to the live app presented in Development Mode.
  • App Studio will overlay the live app running in the Development Mode as a transparent IFRAME element when users choose the controls that will be presented in the empty space surrounding the live app.
    • The Project Explorer and Property Window will show up.
    • A click on the application user interface element will select the relevant configuration node in the explorer hierarchy and show its entire set of properties.
    • The hierarchy of the Project Explorer will present the familiar controllers, pages, and controls. There will be additional nodes that will allow configuration of the global project settings, contents of ~/touch-settings.json, and data models.
  • The Yaml database of all available configuration elements with their properties and learning resources will be included with the App Studio application.
  • The settings available in the Project Wizard today will migrate to the future Project Explorer and Property Window.
  • The RESTful API of App Studio back-end will allow its front-end embedded in a live app to send commands that will modify the host when developers make changes.
  • The service worker of App Studio will monitor the code generation process initiated by developers.

The "Development Mode" is activated in the apps running on the localhost when users choose the "Developer Tools" option in the app context menu. The app is sized to a particular known device . The empty space will provide the real estate for the future Project Explorer and Property Window.
The "Development Mode" is activated in the apps running on the localhost when users choose the "Developer Tools" option in the app context menu. The app is sized to a particular known device . The empty space will provide the real estate for the future Project Explorer and Property Window.

The live Content Hub content is presented with the Property Window on the right-hand side of the screen. The window is the Touch UI survey form driven by JavaScript object describing the available properties. The window will become the primary app configuration tool driven by a YAML database of all known project configuration elements and their properties.
The live Content Hub content is presented with the Property Window on the right-hand side of the screen. The window is the Touch UI survey form driven by JavaScript object describing the available properties. The window will become the primary app configuration tool driven by a YAML database of all known project configuration elements and their properties.

The Property Window will eventually become an alternative form style in Touch UI. Forms with the categories and data fields can be automatically translated into the universal presentation as a collection of properties. Picture the Property Window slide out when a row is selected in Grid or List and you will get the idea.

Live Project Designer v9 will be introduced gradually. Every future 8.9.N.0 release will introduce a more capable App Studio and will culminate with the product renamed to v9.0.0.0.

Bug Fixes and Enhancements

The following new features, bug fixes, and enhancements are included in the release

  • (PWA) Service worker adds the ~/pages/offline resource to the ”'user” cache of the browser. This page is served when a resource is fetched by the browser in the "navigate" mode and the resource fails to fetch successfully.
  • (PWA) Blobs are fetched from the 'offline.blob' object store first and then an attempt is made to fetch them from the server.
  • (Touch UI) The logout process will raise the even with the callback property in the event to make it possible for the custom logout processing. Offline Sync warns the user about the account data removal with the help of this event.
  • (Touch UI) The "logout" user menu items are not available if the device is not online.
  • (Touch UI) Progress screen has been modified to allow the 'marquee' and 'real-time' progress indicator. Offline Sync provides users with the countdown based on the time it took to download the initial front-end and user account data. This allows the user to anticipate the synchronization.
  • (Touch UI) Saved user accounts are not visible in the user menu if the user is working in the offline mode.
  • (Touch UI) Method $app.touch.notify performs safe-html processing for "rich"notifications.
  • (Touch UI) Progress screen is repositioned when the size of the browser window is changed.
  • (Touch UI) Service worker detection works correctly when there is no active service worker.
  • (Framework) Client library now keeps track of BLOBs changed or replaced by the user. The timestamp is added to the BLOB urls to ensure that the fresh thumbnails are presented. Other users will see the fresh thumbnails each 10 minutes. The thunmails are cached for 10 minutes to improve performance of applications.
  • (Framework) The constant Blob.ThumbnailCacheTimeout is changed to the static public variable with the default value of 10. The previous value was set to 5. The thumbnails of images will have a 10 minute caching on the client.
  • (Advanced Search) Field property AutoCompletePrefixLength will require users to enter the specified number of characters for the auto complete to start in the Advanced Search form. Entering a value greater than 1 will result in the improved performance of the applications that require search on large datasets.
  • (Surveys) Property 'autoCompletePrefixLength' can be specified in the question definition.
  • (Framework) Fixed incorrect exclusion of primary key fields that are requested in the FieldFilter requests coming from the client. Previously the advanced search auto-complete lists were missing the field values if they were included in the primary key.
  • (Advanced Search) By default the Advanced Search form configures all text fields to auto-complete with the distinct values. The first field is considered to be a "distinct selector" and therefore the distinct grouping is not performed.
    • Tag the first field as lookup-distinct to yield the group values in the auto-complete of the first field.
    • Tag any other field as lookup-distinct-none to prevent the distinct value counting.
  • (Universal Input) Method $app.input.execute will delay execution if the target form is not visible yet. This may be the case when the reading pane detail form is opening for the first time with the “fade-in” effect.
  • (ODP) Instructions to save the form before Export and Report will be force-displayed to the user. Offline Data Processor keeps the data in memory. Exporting and reporting work with the server-side data. Users will not see their data until the form with the changes is committed. The notification will immediately come up instead of being queued up.
  • (Framework) Client library resource ^FilteSiteMap^ is now replaced with ^Filter^ to provide consistency with the offline mode. Previously the “Filter site map” placeholder was displayed above the table of contents. The “Filter” placeholder is displayed starting with this product update.
  • (Offline Sync) The site map is rendered when the ~/pages/offline resource is requested from the client. Supply your own physical page for an alternative custom content displayed to users. The service worker returns this resource from the “user” cache storage when users navigate to a resource that is either not available due to a lost network or does not exist in the application back-end on the server.
  • (Touch UI) The "Settings" option is not available on the pages marked as offline in the apps with Offline Sync.
  • (Offline Sync) The "Sync" option is available in the context menu of the reading pane master of an offline page in the apps when users are working in the offline mode..
  • (Universal Input) Fixed incorrect positioning of the cursor of the virtual keyboard. Previously the cursor was displayed on the next line below the edited value.
  • (Framework) Removed dependencies on $ since the new Offline Sync does not require an external native support.
  • (Offline Sync) The initial synching is started upon login only if there are offline pages and the user is in the “Offline” role.
  • (Project Wizard) Add-ons can now be installed directly from the Settings | Features | Add-ons section when users press the Install button. The add-ons associated with the user’s organization are fetched from the distribution server.
  • (Offline Sync) Configuration parameter odp.sync.refreshTokenDuration controls the ability of the user with the "Offline" role to refresh the access tokens. The default value is 180 days. Offline users can sync with the server for up to 6 months without being required to sign in again. If the offline user visits an online page of the app then the access and refresh tokens are renewed.
  • (Offline Sync) The null values returned from the server are resolved correctly when the pending changes are processed during synchronization.
  • (Framework) The AutoFill command is executed on the server to allow the postal address verification when the device is online.
  • (Touch UI) Charts, Calendar, and Maps view styles are not available in the offline mode. The "calendar" view style will be re-enabled in the offline mode in the future releases when ODP (Offline Data Processor) is upgraded to handle the aggregation requests. The "charts" view style will be re-enabled in the offline mode when the alternative charting library will replace the Google Charts.
  • (Touch UI) The "promo" button is correctly positioned in the browser-based designer when the Charts or Maps view style is active.
  • (Universal Input) Added the missing html encoding when the lookup is constructed. This prevents the possibility of the script injection in the Advanced Search form.
  • (Code Dom) Fixed the compilation issue in the applications without authentication.
  • (Framework) The new virtual method ApplicationServicesBase.UserCanWorkOffline(string) returns true if the user is allowed to work in the offline mode. The default implementation will ensure that the user has the "Offline" role. Developers can impose additional restrictions by overriding this method.
  • (Framework) Method TransactionManagerBase.Commit(List<ActionArgs>) is renamed to CommitAll.
  • (RESTful) Method $app.restful accepts FormData instances as the value of the body option.
  • (RESTful) Method $app.restful resolves to the “raw” response object if the dataType option is set to 'response'. Otherwise the method resolves to a JSON object or a Blob instance.
  • (RESTful) Method $app.restful always loads the ~/js/restful(.min).js file when invoked from the embedded application pages. It does not require the RESTful API engine to be enabled. The client side framework is being migrated to use $app.restful when making the direct server calls to the application on the server.
  • (Framework) Network calls failed when the device loses the network connection will display a corresponding message to users instead of the cryptic error.
  • (Offline Sync) Users with the offline role are assigned the urn:app:remember-me claim. The login of such users will result in the identity of the user to be persisted regardless of the "Remember Me" configuration and user preference. This allows automatic re-authentication of the user when "online" pages are visited. The "Offline" users will have to logout explicitly to remove their account data from the device.
  • (Touch UI) BLOBs are uploaded with the $app.restful method. This allows automatic access token renewal on the "offline" pages.
  • (ODP) Failed DELETE of a master row will not cause the self-circuiting of transactions on the client, which has required a page reload to resolve.
  • (Framework) New implementation of $app.uploadFileAjax uses the $app.restful() method instead of $.ajax. This eliminates the BLOB uploading errors in the online application when users stay on the page for a long time. Method $app.restful renews the access token automatically, which resolves the issue for online pages and makes possible BLOB synchronization in the offline mode. The original implementation relied on the session cookie, which required users to navigate away from the page to renew.
Friday, January 20, 2023PrintSubscribe
Offline App Step-By-Step

Code On Time offers a unique method of offline application development.

You begin by creating an online app instead. Put together a few data models and your application is ready to execute in any major web browser. You can even have it installed on your device to run natively.

Use the Project Designer to mark the data-aware pages as “offline”. This will enable the atomic data entry in the forms. Master and detail data items are persisted together when the top-level form is saved.

Next enable the Offline Sync Add-on, the optional component that brings about the magic. Users in the “Offline” role will see how the application front-end and account data are downloaded to the device as soon as they sign in. The front-end will look for the data in the local storage when performing the read and write operations. Users will have to explicitly sync their pending changes with the online back-end.

Learn to build offline apps with an online-first approach!

The installed app will also work in the offline mode without any distinguishable differences with the offline mode in a browser.
The installed app will also work in the offline mode without any distinguishable differences with the offline mode in a browser.

Continue to Offline-First Apps