Skip to main content

Implementing Offline Capabilities

1. Detecting Network Status

Property Description Values
navigator.onLine Indicates if the browser is online or offline true / false

Example: Check Network Status

if (navigator.onLine) {
  console.log("Online");
} else {
  console.log("Offline");
}
Note: navigator.onLine is a best-effort indicator and may not reflect actual network availability.

Network Status Detection Methods

  • navigator.onLine - Basic online/offline status
  • HTTP requests - Actual network connectivity check
  • Service Worker - Intercept and handle offline scenarios

2. Implementing Offline Storage

Example: Using SQLite for Local Data Persistence

import { SQLiteDB } from '@ionic-native/sqlite';

const db = await SQLiteDB.create({
  name: 'mydb.db',
  location: 'default'
});

await db.executeSql('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT)');

await db.executeSql('INSERT INTO users (name) VALUES ('John Doe')');
Feature Description Support
SQLite Local SQL database for structured data NEW Ionic 7+
Local Storage Simple key-value storage using browser's localStorage Native support
IndexedDB Advanced client-side storage for large amounts of data EXPERIMENTAL
Note: For hybrid apps, SQLite is preferred over localStorage for complex data structures.
Warning: IndexedDB is not supported in all browsers and may require polyfills.

Storage Options Comparison

  • SQLite: Best for structured relational data, supports transactions
  • Local Storage: Simple key-value pairs, limited to 5MB
  • IndexedDB: Large-scale data storage, complex queries

3. Caching API Responses

Property Description Default Notes
cacheKey Unique identifier for the cached response null Required for caching to work
ttl Time to live in milliseconds 300000 5 minutes by default
strategy Cache strategy to use 'network-first' Options: 'network-first', 'cache-first', 'stale-while-revalidate'

Example: Caching API Response with Ionic

import { HttpClient } from '@angular/common/http';
import { CacheService } from 'ionic-cache';

constructor(private http: HttpClient, private cache: CacheService) {}

getCachedData() {
  return this.cache.get('api-data', () => {
    return this.http.get('https://api.example.com/data');
  }, {
    ttl: 600000, // 10 minutes
    strategy: 'cache-first'
  });
}
Note: The ionic-cache library provides a simple way to cache API responses in Ionic apps. It supports multiple caching strategies and can be easily integrated into existing Angular services.

4. Implementing Data Sync

Example: Basic Data Sync Setup

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class DataService {
  private apiUrl = 'https://api.example.com/data';

  constructor(private http: HttpClient) {}

  async syncData() {
    const localData = await this.getLocalData();
    const serverData = await this.http.get(this.apiUrl).toPromise();

    // Simple diff and sync logic
    const changes = this.calculateChanges(localData, serverData);
    await this.applyChanges(changes);

    return { success: true };
  }

  private async getLocalData() {
    // Fetch from local storage or database
    return JSON.parse(localStorage.getItem('localData') || '[]');
  }

  private calculateChanges(local, server) {
    // Logic to detect differences
    return { added: [], updated: [], deleted: [] };
  }

  private async applyChanges(changes) {
    // Apply changes to local storage or database
    localStorage.setItem('localData', JSON.stringify(changes));
  }
}
Feature Description Status
syncStrategy Defines how data is synchronized (e.g., push, pull, bidirectional) NEW
conflictResolution How to handle data conflicts (e.g., last-write-wins, manual) DEPRECATED
syncInterval How often to sync data (in milliseconds) NEW

Sync Workflow

  1. Check network status
  2. Fetch local changes
  3. Compare with server data
  4. Resolve conflicts if any
  5. Push local changes to server
  6. Update local storage with server data

Data Sync Strategies

  • Push Only: Local changes are sent to server, no pull
  • Pull Only: Server data is fetched, local changes ignored
  • Bidirectional: Both push and pull with conflict handling
Note: Use Network plugin to detect offline/online status in Ionic apps.
Warning: Always implement conflict resolution to prevent data loss.

5. Handling Offline Forms

Property Description Default Notes
formId Unique identifier for the form null Required for tracking offline submissions
offlineStorageKey Storage key for local persistence 'ionic-offline-forms' Used to store form data in IndexedDB or localStorage
autoSaveInterval Time (in ms) between auto-saves 5000 Set to 0 to disable auto-save

Example: Basic Offline Form Setup

import { IonicForm } from '@ionic/offline';

const form = new IonicForm({
  formId: 'user-profile',
  offlineStorageKey: 'user-data',
  autoSaveInterval: 3000
});

form.onSave((data) => {
  console.log('Saved offline data:', data);
});

Example: Submitting Offline Form Data

async function submitForm(formId) {
  const form = new IonicForm({ formId });
  const data = await form.getOfflineData();

  if (navigator.onLine) {
    // Submit to server
    const response = await fetch('/api/submit', {
      method: 'POST',
      body: JSON.stringify(data)
    });
    await form.clearOfflineData();
  } else {
    // Store for later
    await form.saveOffline(data);
  }
}
Note: Ensure that form data is properly serialized and validated before storing offline.
Warning: Do not store sensitive data in local storage without encryption.

Key Considerations

  • Implement proper data validation before saving offline
  • Use unique formId to prevent data conflicts
  • Handle network state changes gracefully
  • Clear offline data after successful submission

6. Using Offline-First Architecture

Offline-first architecture ensures that your Ionic application functions seamlessly even when network connectivity is lost. This approach prioritizes local data storage and synchronization, making your app resilient to network interruptions.

Feature Description Implementation
Local Data Storage Stores data locally using IndexedDB or SQLite for offline access. const db = new Dexie('myAppDB');
Sync Strategy Manages data synchronization when the device reconnects to the network. syncData();
Cache Management Handles caching of API responses to reduce network requests. cache.get('endpoint');

Example: Implementing Local Storage with Dexie.js

import Dexie from 'dexie';

class MyDatabase extends Dexie {
  constructor() {
    super('MyAppDB');
    this.version(1).stores({
      items: '++id, name, description'
    });
  }
}

const db = new MyDatabase();

// Add item to local storage
db.items.add({ name: 'Item 1', description: 'Description of item 1' });

// Retrieve items
db.items.toArray().then(items => {
  console.log(items);
});
Note: Dexie.js is a popular library for IndexedDB management in web applications.

Offline-First Workflow

  1. Application loads and initializes local storage
  2. User interacts with the app while offline
  3. Changes are stored locally in IndexedDB or SQLite
  4. When network is restored, sync process begins
  5. Data is synchronized with the backend server

7. Implementing Conflict Resolution

Conflict Type Description Solution Approach Tools/Methods
Data Overwrite Conflicts Multiple users edit the same data, leading to one update overwriting another. Implement version control or timestamps to detect and resolve updates. etag, lastModified
Merge Conflicts Changes from different sources need to be merged, often in a database or file system. Use merge strategies like 3-way merge or conflict markers for manual review. git merge, merge strategies
Offline Sync Conflicts Changes made offline are synced when connectivity is restored, causing conflicts. Queue changes and apply conflict resolution logic during sync. ionic offline, sync queue

Example: Handling Data Overwrite Conflicts

const handleOverwriteConflict = (localData, serverData) => {
  if (localData.version < serverData.version) {
    // Server version is newer, apply it
    return serverData;
  } else if (localData.version > serverData.version) {
    // Local version is newer, push it to server
    return localData;
  } else {
    // Versions match, no conflict
    return null;
  }
};

Example: Using ETags for Conflict Detection

const fetchWithEtag = async (url, etag) => {
  const response = await fetch(url, {
    headers: { 'If-None-Match': etag }
  });
  if (response.status === 304) {
    // No changes, use cached data
    return { changed: false };
  } else {
    const data = await response.json();
    return { changed: true, data, etag: response.headers.get('ETag') };
  }
};
Note: ETags are a simple yet effective way to detect changes on the server side without transferring full data.
Warning: Manual conflict resolution can lead to data loss if not handled carefully. Always log conflicts for review.

8. Caching Static Assets

Property Description Default Notes
cache Enables caching of static assets false Set to true to enable caching
maxAge Maximum age of cached assets in milliseconds 86400000 1 day default cache duration
strategy Cache strategy to use (e.g., cache-first, network-first) cache-first Choose appropriate strategy for asset type

Example: Configure Static Asset Caching

const config = {
  cache: true,
  maxAge: 86400000,
  strategy: 'cache-first'
};

// Apply to Ionic app build process
Note: Caching static assets improves load times and reduces network usage for repeat visits.
Warning: Ensure cache invalidation is handled properly to avoid serving stale content.

9. Displaying Offline UI Indicators

Example: Basic Offline Indicator

import { Network } from &#x27;@capacitor/network&#x27;;

async function checkNetworkStatus() {
  const status = await Network.getStatus();
  const offlineIndicator = document.getElementById(&#x27;offline-indicator&#x27;);

  if (!status.connected) {
    offlineIndicator.style.display = &#x27;block&#x27;;
  } else {
    offlineIndicator.style.display = &#x27;none&#x27;;
  }
}

Network.addListener(&#x27;networkStatusChange&#x27;, checkNetworkStatus);
Property Description Values
connected Indicates if the device is connected to the internet true or false
connectionType Type of network connection (wifi, cellular, etc.) wifi, cellular, none
Note: The Network plugin requires the @capacitor/network package to be installed.

Example: Offline Indicator with Toast Notification

import { Network } from &#x27;@capacitor/network&#x27;;
import { Toast } from &#x27;@capacitor/toast&#x27;;

async function showOfflineToast() {
  const status = await Network.getStatus();
  if (!status.connected) {
    await Toast.show({
      message: &#x27;You are offline. Some features may not work.&#x27;,
      duration: &#x27;long&#x27;
    });
  }
}

Workflow: Implementing Offline UI

  1. Use Network.getStatus() to check connection status
  2. Display a visual indicator (e.g., banner, toast) when offline
  3. Hide the indicator when connection is restored
  4. Update UI elements to reflect offline state (e.g., disable actions)

10. Testing Offline Behavior

Example: Mocking Network Conditions

import { TestBed } from '@angular/core/testing';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { Network } from '@ionic-native/network/ngx';

describe('Network Service', () => {
  let network: Network;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientTestingModule],
      providers: [Network]
    });
    network = TestBed.inject(Network);
  });

  it('should detect offline state', () => {
    spyOn(network, 'onDisconnect').and.returnValue(Promise.resolve());
    expect(network.onDisconnect).toHaveBeenCalled();
  });
});
Method Description Return Type
onDisconnect() Triggers when device goes offline Promise<void>
onConnect() Triggers when device reconnects Promise<void>
Note: Use Network plugin to simulate offline conditions in tests.

Testing Approaches

  • Unit Tests: Mock network state using spyOn
  • Integration Tests: Use ionic serve with network throttling tools
  • E2E Tests: Use browser dev tools or external tools like Charles Proxy

Was this article helpful?