The “Offline First” Approach to Cross-Platform App Development

Morgan C. Benton

Associate Professor, ISAT, James Madison University

Valley Tech Con 2019

https://valleytechcon.com

FAILURE
TO
LAUNCH

Intermittent connectivity is just a fact of modern life.

 

As developers, we would be remiss
if we didn't admit the problem and address it head on.

WE CAN’T KEEP BUILDING APPS WITH THE DESKTOP MINDSET OF PERMANENT, FAST CONNECTIVITY, WHERE A TEMPORARY DISCONNECTION OR SLOW SERVICE IS REGARDED AS A PROBLEM AND COMMUNICATED AS AN ERROR.

--Alex Feyerke, "Say Hello to Offline First", November 2013

Key Characteristics

  • Reduce overall data
    • Small, optimized files
    • Smaller number of files
  • Client-side processing
    • For the web => SPAs
    • Native apps
  • Replicate the database*

* One database per customer!?!?

1. Everyone doesn't
     need the entire DB.
2. This kind of data
     management does
     not apply to every
     scenario.

What are the key data management strategies?

Data Replication Strategies

  • Snapshot
  • Realtime
  • Optimistic Mutation
  • Own-data & Own-net
  • Sync-data & Sync-net
  • Time Travel

Snapshot

Snapshot distributes data exactly as it appears at a specific moment in time and does not monitor for updates to the data. When synchronization occurs, the entire snapshot is generated and sent to client service.

Good when:

  • Data changes infrequently
  • Out-of-date copies of data are acceptable
  • Replicating small or medium volumes of data
  • A large volume of changes occurs over a short period of time
  • Keep the current values after having lost connection for some time

Examples: product catalog, speakers at a conference

Realtime

Realtime typically starts with a snapshot of the remote database data. As soon as the initial snapshot is taken, subsequent data changes made at the remote are delivered to the client as they occur (in near real time). The data changes are applied at the client in the same order as they occurred at the remote.

Good when:

  • Clients need incremental changes as they occur
  • The app requires low latency between API and client changes
  • The app requires access to intermediate data states, not simply to the net data change to the row
  • The API has a very high volume of create, update, patch, and remove activity

Examples: stock ticker, live sporting events

Realtime + Optimistic Mutation

In "normal" realtime, data originating on a client must be processed on the remote API before being replicated to the local database. "Optimistic" mutation modifies the local database before remote processing happens and maybe changes it later, if necessary.

Good when:

  • Mutations made on a client are not likely to be rejected or changed on the remote API
  • Some delay is acceptable

Examples: chat or text messaging

Own-data & Own-net

The data is unique to the user, and (usually) not changeable by the remote. Local mutations are queued until the next time the client is connected. Own-data queues every change and processes them in order on the remote. Own-net only queues the net change to a record, and only the net changes are sent to the remote.

Good when:

  • The app data is largely or entirely owned by the app user
  • Long periods of disconnection are acceptable

Examples: user profiles or personal productivity apps

Sync-data & Sync-net

The data is owned by multiple users, and conflicts must be resolved by the remote. Each mutation sent to the remote must contain a "before and after" so that if multiple clients ask to mutate the same records at about the same time, some conflict resolution strategy can be employed. With sync-data each queue entry contains original plus >=1 ordered mutations. With sync-net, each queue entry contains original plus the net change at the time the mutation request is sent to the remote.

Good when:

  • Multiple entities may mutate the same record at nearly the same time

Examples: multi-user shared document editing

Time Travel

All mutations of all records are stored indefinitely or for a "long time" so that it is possible to "time travel" to see the state of a record at any given point in time. Since this will likely require a large amount of storage, it may only be appropriate when the clients have a great deal of storage capacity. Alternatively, once the queue is stored on the remote, the local replicant may only need to store pointers to each of the mutations.

Good when:

  • It is important to be able to see the state of a record at any point throughout the history of its existence

Examples: git repositories, undo queues for documents

Demo Time

The Valley Tech Con 2019 app allows users to view the conference schedule, details about speakers, talks, and sponsors, as well as rating the events and tweeting about them.

 

Tech Stack:

API--FeathersJS

App--VueJS + Vuetify

Design Considerations

  • Conference goers may not have a stable internet connection and/or be reluctant to use data
  • The app must be simple to use since there's not any time for training or to learn complex interactions
  • Data strategies used:
    • Snapshot for info about agenda, speakers, and sponsors
    • Realtime with optimistic mutation for ratings of talks

How It Works

  • Users access the app via a web browser
    • Android users are prompted to download and install the app on their home screen
    • iOS users must know how to add an app to the home screen, and explicitly choose to do so
  • On first load, all HTML, CSS, JS, and images are downloaded and cached; the conference data is synced to an IndexedDB running in the browser

Offline Mode

  • If the app loses internet connectivity, content is served from the local cache
  • Event ratings are optimistically stored locally and update the app interface (should be, anyway...)
  • If there is no connection, the rating request is queued until there is
  • If an event rating request is rejected for any reason, the local database is updated to revert the prior mutation

PWA

  • Inherently cross-platform because => HTML, CSS, JS
  • No app stores
  • Fewer clicks to install
  • Easier to update
  • Has access to most, but not all of the phone's hardware
  • May not perform as smoothly
  • Harder to monetize?

Native App

  • Must be built or compiled separately for each OS
  • Subject to app store approval
  • Harder to install/update
  • Can access every bit of the hardware, including bleeding edge features
  • Solid performance
  • Can be sold, but at a cost

PWAs vs Native Apps

Do people really use apps?

People spend HUGE amounts of time on their phones, but...

  • Over 50% download zero apps/month
  • Once downloaded, 21% abandon after only 1 use
  • 77% abandon after 72 hours!
  • 84% of app time is consumed by just 5 apps
  • $6 latte? No problem. $0.99 app? Highway robbery!

 

Can apps be powerful? Yes. But you have to be very careful and very strategic about how they're used. Paying attention to user experience, including offline utility, is part of that.

Conclusion

Building good apps is hard.
 

The "offline first" approach to designing and building apps should be a part of your process.

Thank You!

Questions?

morgan.benton@gmail.com

https://morphatic.com

https://github.com/morphatic

Made with Slides.com