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
    React Native
    Connect.Tech 2019

    View Slide

  2. Tim Whitacre
    Atlanta
    Engineering Manager @ New Story Charity

    @timwco ✨
    @newstorycharity

    View Slide

  3. We pioneer solutions to end global homelessness.
    newstorycharity.org

    View Slide

  4. O!ine First

    View Slide

  5. 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

    View Slide

  6. Why O!ine First
    » Connected & Powered => Disconnected & Battery
    » Geography plays an important role
    » O!ine is simply a fact of life.
    !

    View Slide

  7. Why Now?
    When else are you going to think about it?

    View Slide

  8. Benefits
    » Avoid Local Data Loss
    » Speed Up Your App
    » Your App Runs Everywhere

    View Slide

  9. Pitfalls
    » Data Conflicts
    » Diverse Data Types
    » Implementation Time
    » Others 2
    2 https://alistapart.com/article/o"ine-first

    View Slide

  10. O!ine Syncing Options
    » Real Time
    » Data Syncs Upon Connection
    » Progressive Enhancement
    » Manual
    » Manually preform a sync operation
    » Architecture Strategy

    View Slide

  11. O!ine first requires a mix of UX and Development
    Strategies. This includes both frontend and backend
    solutions.

    View Slide

  12. Please don't skip the UX part.

    View Slide

  13. How Do O!ine Apps Work?

    View Slide

  14. Local Storage
    !

    View Slide

  15. {
    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...?

    View Slide

  16. O!ine Tools
    » GraphQL
    » Redux O!ine/Persist
    » Realm

    View Slide

  17. GraphQL
    Only get the data that we need. Less queries, less operations and quicker access to data.

    View Slide

  18. 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
    }
    }
    `;

    View Slide

  19. Redux O!ine/Persist
    Redux O!ine allows us to store and "rehydrate" our state as needed.

    View Slide

  20. // 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 };
    };

    View Slide

  21. // 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 (
    // Standard Redux Provider
    // Delays rendering of app



    );
    }
    }

    View Slide

  22. 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
    !

    View Slide

  23. // 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

    View Slide

  24. // 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);
    };

    View Slide

  25. // 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 (





    );
    }
    }

    View Slide

  26. 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');

    View Slide

  27. 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');
    }

    View Slide

  28. Pitfalls
    » Maintenance (Active Development)
    » Dependency & Upgrades

    View Slide

  29. Review
    » UX/UI Strategy
    » Local Data Loss (Realm)
    » Global State Persistence (Redux O!ine/Persist)
    » Diverse Data Types (Realm, GraphQL)
    » Data Conflicts (Realm, GraphQL)

    View Slide

  30. Thank You
    !
    l.timw.co/ctech2019

    @timwco ✨
    @newstorycharity

    View Slide

  31. Image Credits
    » Devopedia
    » @andreee
    » New Story Charity

    View Slide