Upgrade to Pro — share decks privately, control downloads, hide ads and more …

TGIF Offline-first

TGIF Offline-first

Introduction to building offline-first web apps.

Online version:
http://codekult.github.io/tgif-offline-first

Diego Calderón

January 30, 2015
Tweet

More Decks by Diego Calderón

Other Decks in Programming

Transcript

  1. Offline needs UX Love ❤ Christian Heilmann. The next UX

    challenge on the web Handling conflicts between different content versions. Reempting user's needs. Displaying chronological data updates. … just to name a few.
  2. We need to think about the advantages and problems of

    this approach and set a few things to focus on, in order to create a better user experience.
  3. Build security and close the gap beetwen native an web

    apps save vs sync. Offline-first advantages
  4. Web storage l o c a l S t o

    r a g e and s e s s i o n S t o r a g e are both instances of the S t o r a g e object, and function in a similar way, saving data based on named key/value pairs. l o c a l S t o r a g e persists when the browser is closed and reopened, in turn s e s s i o n S t o r a g e endures for the duration of the page session.
  5. Set data: l o c a l S t o

    r a g e . s e t I t e m ( ' c o l o r ' , ' b e i g e ' ) ; Get data: l o c a l S t o r a g e . g e t I t e m ( ' c o l o r ' ) ; Remove/clear data: l o c a l S t o r a g e . r e m o v e I t e m ( ' c o l o r ' ) ; l o c a l S t o r a g e . c l e a r ( ) ; / / C l e a r a l l
  6. All the data in web storage is saved as strings

    We can work around this using J S O N . s t r i n g i f y ( ) and J S O N . p a r s e ( ) methods for objects: l o c a l S t o r a g e . s e t I t e m ( ' m y O b j ' , J S O N . s t r i n g i f y ( m y O b j ) ) ; J S O N . p a r s e ( l o c a l S t o r a g e . g e t I t e m ( ' m y O b j ' ) ) ;
  7. Or coerce the data into the expected JavaScript datatype: v

    a r q t y = p a r s e I n t ( l o c a l S t o r a g e . g e t I t e m ( ' q t y ' ) ) ;
  8. We can track when storage area changes using the s

    t o r a g e event, which is fired on w i n d o w object whenever s e t I t e m ( ) , r e m o v e I t e m ( ) , or c l e a r ( ) is called and actually changes something.
  9. S t o r a g e E v e

    n t object k e y : The changed key. o l d V a l u e : Previous value (now overwritten), or `null` if a new item was added. n e w V a l u e : New value, or `null if an item was removed. u r l : The page which called a method that triggered this change.
  10. IndexedDB IndexedDB is a transactional, object-oriented and noSQL database hosted

    and persisted in the browser. First, some preliminal concepts:
  11. Database Each database has a name and a current version,

    which starts at 1 but can be specified. A database can have only version at a given time and we can change it by open the database with a greater number version. This will start a v e r s i o n c h a n g e transaction and fire an u p g r a d e n e e d e d event, and its handler is the only place where the schema of the database can be updated. A database is composed of one or more:
  12. Object store The mechanism by which data is stored in

    the database. Holds key-value pair records. Those records are sorted according to the keys in an ascending order. Must have an unique name within the database and can have a key generator, a key path, both, or none of them.
  13. Transaction This is how we interact with the data in

    a database, everything we change happens in the context of a transaction. There are three modes of transactions: r e a d W r i t e , r e a d O n l y and v e r s i o n C h a n g e . A database connection can have multiple transactions at a a time, so long as the transactions don't have overlapping scopes.
  14. Open a database: v a r d b , r

    e q u e s t = i n d e x e d D B . o p e n ( " f r o n t e n d D a t a b a s e " ) ; r e q u e s t . o n e r r o r = f u n c t i o n ( e v e n t ) { a l e r t ( " E r r o r " ) ; } ; r e q u e s t . o n s u c c e s s = f u n c t i o n ( e v e n t ) { d b = e v e n t . t a r g e t . r e s u l t ; } ; This will return an special type of request (I D B O p e n D B R e q u e s t ) with an error, or a database as result if success.
  15. If the database doesn't exists or if the database exists

    but and greater version number is specified, an o n u p g r a d e n e e d e d event is fired. We can create the schema for the new database or update the existent one in the o n u p g r a d e n e e d e d event handler.
  16. Create/update the schema of a database: r e q u

    e s t . o n u p g r a d e n e e d e d = f u n c t i o n ( e v e n t ) { v a r d b = e v e n t . t a r g e t . r e s u l t ; / / C r e a t e a n o b j e c t S t o r e f o r t h i s d a t a b a s e v a r o b j e c t S t o r e = d b . c r e a t e O b j e c t S t o r e ( " d e v s " , { k e y P a t h : " d n i " } ) } ; Creating an objectStore with a key generator: v a r o b j e c t S t o r e = d b . c r e a t e O b j e c t S t o r e ( " d e v s " , { a u t o I n c r e m e n t : t r u e
  17. Created Object stores persist through versions. Here we can create

    new object stores, delete unnecesary ones, or change an existing object store (deleting the old and creating a new one, saving the necessary data). If the o n u p g r a d e n e e d e d event exits successfully, the o n s u c c e s s handler of the open database request will be triggered.
  18. Add/remove/update values in an object store Open a transaction: v

    a r t r a n s a c t i o n = d b . t r a n s a c t i o n ( [ " d e v s " ] , " r e a d w r i t e " ) ; Transactions can receive DOM events of three different types: e r r o r , a b o r t , and c o m p l e t e .
  19. t r a n s a c t i o

    n . o n c o m p l e t e = f u n c t i o n ( e v e n t ) { a l e r t ( " D o n e ! " ) ; } ; t r a n s a c t i o n . o n e r r o r = f u n c t i o n ( e v e n t ) { / / H a n d l e e r r o r s } ; v a r o b j e c t S t o r e = t r a n s a c t i o n . o b j e c t S t o r e ( " d e v s " ) ; f o r ( v a r i i n d e v D a t a ) { v a r r e q u e s t = o b j e c t S t o r e . a d d ( d e v D a t a [ i ] ) ; r e q u e s t . o n s u c c e s s = f u n c t i o n ( e v e n t ) { / / e v e n t . t a r g e t . r e s u l t = = d e v D a t a [ i ] . d n i ; } ; }
  20. Methods to manipulate data in object stores are: a d

    d ( ) , c l e a r ( ) , d e l e t e ( ) , g e t ( ) , and p u t ( ) .
  21. Shorthand example: d b . t r a n s

    a c t i o n ( " d e v s " ) . o b j e c t S t o r e ( " d e v s " ) . g e t ( " 3 3 2 2 2 1 1 1 " ) . o n s u c c e s s = f u n c t i o n ( e v e n t ) { a l e r t ( " N a m e f o r D N I 3 3 2 2 2 1 1 1 i s " + e v e n t . t a r g e t . r e s u l t . n a m e ) ; } ;
  22. Indexes and cursors An index is a specialized object store

    for looking up records in another object store, called the referenced object store. v a r o b j e c t S t o r e = d b . c r e a t e O b j e c t S t o r e ( " d e v s " , { k e y P a t h : " d n i " } ) , / / C r e a t e a n i n d e x t o s e a r c h d e v s b y n a m e . W e m a y h a v e d u p l i c a t e s / / s o w e c a n ' t u s e a u n i q u e i n d e x . i n d e x = o b j e c t S t o r e . c r e a t e I n d e x ( " n a m e " , " n a m e " , { u n i q u e : f a l s e } ) i n d e x . g e t ( " A l e x " ) . o n s u c c e s s = f u n c t i o n ( e v e n t ) { a l e r t ( " A l e x ' s D N I i s " + e v e n t . t a r g e t . r e s u l t . d n i ) ; } ;
  23. A cursor is a mechanism for iterating over multiple records,

    with an optional key range. v a r d e v s = [ ] ; o b j e c t S t o r e . o p e n C u r s o r ( ) . o n s u c c e s s = f u n c t i o n ( e v e n t ) { v a r c u r s o r = e v e n t . t a r g e t . r e s u l t ; i f ( c u r s o r ) { d e v s ( c u r s o r . v a l u e ) ; c u r s o r . c o n t i n u e ( ) ; } e l s e { a l e r t ( " A l l d e v s : " + d e v s ) ; } } ;
  24. We can build a key range with: o n l

    y ( ) , u p p e r B o u n d ( ) , l o w e r B o u n d ( ) , b o u n d ( ) . Bounds methods accept a boolean parameter to specify is the bounds is included in the cursor (`false` include and `true` doesn't). Example: v a r k e y R a n g e E x a m p l e = I D B K e y R a n g e . b o u n d ( " 3 3 2 2 2 1 1 1 " , " 3 4 3 3 3 2 2 2 " , t r u e
  25. AppCache Allows a developer to specify which files the browser

    should cache and make available to offline users.
  26. To enable the application cache for an app, include the

    manifest attribute on the document's html tag: m a n i f e s t = " e x a m p l e . a p p c a c h e " The manifest attribute should be included on every page of your web application that you want cached.
  27. A manifest file must be served with the mime-type text/cache-manifest

    in order to to work in older browsers and IE11. You may need to add a custom file type to your web server or . h t a c c e s s configuration.
  28. A simple manifest looks something like this: C A C

    H E M A N I F E S T i n d e x . h t m l s t y l e s h e e t . c s s i m a g e s / l o g o . p n g s c r i p t s / m a i n . j s h t t p : / / c d n . e x a m p l e . c o m / s c r i p t s / m a i n . j s
  29. And a more complex example: C A C H E

    M A N I F E S T # 2 0 1 0 - 0 6 - 1 8 : v 2 # E x p l i c i t l y c a c h e d ' m a s t e r e n t r i e s ' . C A C H E : / f a v i c o n . i c o i n d e x . h t m l s t y l e s h e e t . c s s i m a g e s / l o g o . p n g s c r i p t s / m a i n . j s # R e s o u r c e s t h a t r e q u i r e t h e u s e r t o b e o n l i n e . N E T W O R K : *
  30. CACHE: This is the default section for entries. Files listed

    under this header (or immediately after the C A C H E M A N I F E S T ) will be explicitly cached after they're downloaded for the first time.
  31. NETWORK: Files listed in this section may come from the

    network if they aren't in the cache, otherwise the network isn't used, even if the user is online. You can white-list specific URLs here, or simply * , which allows all URLs. Most sites need * .
  32. FALLBACK: An optional section specifying fallback pages if a resource

    is inaccessible. The first URI is the resource, the second is the fallback used if the network request fails or errors. Both URIs must from the same origin as the manifest file. You can capture specific URLs but also URL prefixes. i m a g e s / l a r g e / will capture failures from URLs such as i m a g e s / l a r g e / w h a t e v e r / i m g . j p g .
  33. Updating the cache Once an application is offline it remains

    cached until one of the following happens: The user clears their browser's data storage for your site. The manifest file is modified. Note: updating a file listed in the manifest doesn't mean the browser will re-cache that resource. The manifest file itself must be altered.
  34. Gotchas: Files always come from the appcache, even online. The

    appcache ony updates if the manifest content has changed. Appcache is an additional cache, not altenative one. Non cached resources will not load on a cached page. and many more.
  35. ServiceWorker Service Worker is a kind of Web Worker. It

    runs in the browser's background, separate from the web page and has the ability to intercept and handle network requests, including programmatically managing a cache of responses. It cannot access the DOM. Instead, it will use p o s t M e s s a g e to communicate with the pages and make extensive use of promises. But this is subject of another talk ¯\_(ツ)_/¯
  36. Examples: MDN - L o c a l S t

    o r a g e Demo (using S t o r a g e E v e n t ) MDN - Using IndexedDB HTML5 Rocks - A simple ToDo list with IndexedDB Treehouse blog - Another ToDo app with IndexedDB HTML5 Rocks - A Beginner's Guide to Using the Application Cache MDN - Using the application cache MDN - Simple Service Worker demo
  37. Further reading: A List Apart - Designing Offline-First Web Apps

    HTML5 Rocks - Storage MDN - Indexed DB WHATWG - AppCache Specification A List Apart - Application Cache is a Douchebag Is Service Worker ready? Hoodie PouchDB Offline-first resources compilation Try on Google