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

Offline First React Native

Offline First React Native

In this talk, we'll take a look at why offline first matters and go deep on some strategies to help you build the performant offline first mobile apps.

Tim Whitacre

October 18, 2019
Tweet

More Decks by Tim Whitacre

Other Decks in Technology

Transcript

  1. O!ine first is an approach to software development in which

    developers build an application's core features to function with or without an internet connection. ... This is especially important for end users who travel and experience internet coverage blind spots. 1 1 https://whatis.techtarget.com/definition/o"ine-first
  2. Why O!ine First » Connected & Powered => Disconnected &

    Battery » Geography plays an important role » O!ine is simply a fact of life. !
  3. Benefits » Avoid Local Data Loss » Speed Up Your

    App » Your App Runs Everywhere
  4. Pitfalls » Data Conflicts » Diverse Data Types » Implementation

    Time » Others 2 2 https://alistapart.com/article/o"ine-first
  5. O!ine Syncing Options » Real Time » Data Syncs Upon

    Connection » Progressive Enhancement » Manual » Manually preform a sync operation » Architecture Strategy
  6. O!ine first requires a mix of UX and Development Strategies.

    This includes both frontend and backend solutions.
  7. { org_1234: { app_status: { last_upload: '2019-09-03T20:59:05+00:00' }, admin_user: {

    name: 'Stacy', token: 'ABCDEF123456' }, data: { ... } }, org_5678: { app_status: { last_upload: '2019-10-17T21:33:01+00:00' }, admin_user: { name: 'Ikenna', token: 'HIJKLMNO789' }, data: { ... } } } See a potential problem...?
  8. GraphQL Only get the data that we need. Less queries,

    less operations and quicker access to data.
  9. export const SYNC_DOWNLOAD = gql` query Download($lastSyncedAt: ISO8601DateTime) { posts(lastSyncedAt:

    $lastSyncedAt) { id title content comments { id content } } authors(lastSyncedAt: $lastSyncedAt) { id first_name last_name } categories(lastSyncedAt: $lastSyncedAt) { id title } } `;
  10. // Store Configure import { createStore, applyMiddleware, compose } from

    'redux'; import { persistStore, persistReducer } from 'redux-persist'; import storage from 'redux-persist/lib/storage'; import { createOffline } from '@redux-offline/redux-offline'; import offlineConfig from '@redux-offline/redux-offline/lib/defaults'; import reducers from './reducers'; const { middleware, enhanceReducer, enhanceStore } = createOffline({ ...offlineConfig, persist: false }); export default key => { const persistConfig = { key, storage, blacklist: ['offline', 'user'] }; const persistedReducer = persistReducer(persistConfig, enhanceReducer(reducers)); const store = createStore( persistedReducer, compose( enhanceStore, applyMiddleware(middleware))); const persistor = persistStore(store); return { store, persistor }; };
  11. // App.js import { Provider as ReduxProvider } from 'react-redux';

    import { PersistGate } from 'redux-persist/integration/react'; import configureStore from 'utilities/configure-store'; class App extends Component { render () { const orgId = getOrganizationId(); const { store, persistor } = configureStore(orgId); return ( <ReduxProvider store={store}> // Standard Redux Provider <PersistGate persistor={persistor}> // Delays rendering of app <MainAppNavigation /> </PersistGate> </ReduxProvider> ); } }
  12. Realm Realm Database is a fast, easy to use, and

    open source alternative to SQLite and Core Data. » Mobile First » Simple API, Define Schemas » Faster than SQLite » Lazy Queries » Data Migrations !
  13. // Realm Models // models.js const PostSchema = { name:

    'Post', properties: { title: 'string', content: 'string', comments: 'Comment[]', tags: 'Tag[]', categories: 'Category[]', author: 'Author', } }; const CommentSchema = { name: 'Comment', properties: { author: 'Author', comment: 'string', approved: 'bool' } }; // Tag Schema // Author Schema // Category Schema
  14. // Realm Setup // realm-setup.js import models from './db/models'; //

    { Posts, Comments, Tags, Categories } const database = async (orgId, schemaVersion) => { if (orgId === 'guest') return null; // If no user, no need for a database const schema = Object.values(models); const config = { schema, path: `${orgId}.realm`, schemaVersion }; return await Realm.open(config); };
  15. // App.js import realmSetup from 'utilities/realm-setup'; // Import Realm class

    App extends Component { render () { const schemaVersion = 1; const orgId = getOrganizationId(); const realm = realmSetup(orgId, schemaVersion); return ( <ReduxProvider store={store}> <PersistGate persistor={persistor}> <MainAppNavigation screenProps={{ realm }} /> </PersistGate> </ReduxProvider> ); } }
  16. Realm Queries // Basic Query const posts = realm.objects('Post'); posts.forEach(p

    => console.log(p.title)); // Posts Sorted Alphabetically const sortedPosts = posts.sorted('title'); // Specific Query (Comments Are Approved) const comments = realm.objects('Comment'); const posts_comments = comments.filtered('approved = true');
  17. Realm Writes // New Comment const author = realm.objectForPrimaryKey('Author', 1);

    const newComment = { author, comment: 'Offline is really cool' }; // Create On it's Own realm.write(() => realm.create('Comment', newComment)); // Create with Relationship const post = realm.objectForPrimaryKey('Post', 291); try { realm.write(() => { post.comments.push(newComment)}); } catch (e) { console.log('Error Upon Creation'); }
  18. Review » UX/UI Strategy » Local Data Loss (Realm) »

    Global State Persistence (Redux O!ine/Persist) » Diverse Data Types (Realm, GraphQL) » Data Conflicts (Realm, GraphQL)