ابرووا
شروع رایگان
☰ فهرست مستندات · ماندگاری آفلاین
به‌زودی. Abrova Sync به‌زودی فعال می‌شود. این صفحه توضیح می‌دهد چطور کار خواهد کرد؛ هنوز قابل استفاده نیست.
این صفحه هنوز ترجمه نشده — نسخه‌ی انگلیسی نمایش داده می‌شود. English

Offline Persistence

Enable offline-first AbrovaSync. Local cache, optimistic updates, queued writes retried until they go through, refused writes rolled back, conflict resolution, and auto-sync on reconnect. Available on Flutter, React Native, Web, Android, and iOS.

به‌روزرسانی: ۵ آبان ۱۴۰۵

AbrovaSync supports offline-first architecture. Enable offline mode to cache data locally and continue working without network connectivity. All changes are automatically synced when the connection is restored.

Quick Start Guide

  1. 1Enable offline mode — Set offlineEnabled: true in your config
  2. 2Cache size — offlineCacheSizeMb is reserved; not used by the SDKs
  3. 3Choose conflict strategy — Pick serverWins, clientWins, merge, or manual
  4. 4Listen to sync state — Monitor onSyncState for idle/syncing/offline/error
  5. 5Use normally — All CRUD operations work offline automatically!

Optimistic Updates

While offline, writes return immediately with local data

Auto Sync

Pending changes sync automatically when online

Conflict Resolution

Choose strategy: Server wins, Client wins, Merge, or Manual (Android / iOS)

Local Cache

Documents you read and write are kept on the device

Conflict Resolution Strategies#

When the same document is edited on a device that's offline and on the server at the same time, AbrovaSync needs a tie-break rule. Pick the strategy that matches your data model:

serverWins

Server version always wins. Best for shared data where server is source of truth.

clientWins

Client version always wins. Best for personal data or when offline edits should persist.

merge

Automatically merge non-conflicting fields. Best for documents with independent fields.

manual

Your app decides via callback (a conflictResolver on Android and iOS only). Best when you need custom merge logic.

For a deeper guide on picking and tuning conflict strategies, see Conflict Resolution.

Use Cases#

Shopping Cart Sync

Users can add items to cart while offline. Use merge strategy to combine items added on multiple devices.

Order Tracking

Display order status even without network. Use serverWins since order status is controlled by backend.

Inventory Management

For inventory-sensitive operations, use serverWins and validate stock levels on sync.

Optimistic Updates Explained#

Optimistic updates keep your app working without a connection: while the device is offline, a write is saved locally and returned immediately, then synced in the background once the connection is back. While online, writes go to the server first. After you call disconnect(), the SDK treats the device as offline: writes are queued at once.

1
User Action
2
Local Update
3
UI Updated
4
Background Sync

User sees instant feedback (step 3) while sync happens in background (step 4)

When the cache is used#

The cache stands in for the server only when the server cannot be reached: no network, a timeout, or a server error. Then reads are answered from the cache, and writes are saved on the device and queued.

When the server answers and refuses the request (your security rules, invalid data), the call fails with that error, even with offline persistence on. Reads are not answered from the cache, and the write is not queued. Check statusCode on the error (403 when the rules refused); see Platform Notes.

SituationReadWrite
Server reached and allows the requestFrom the serverSaved on the server
Server cannot be reached (no network, timeout, server error), or after disconnect()From the cacheSaved on the device and queued
Server refuses the request (security rules, invalid data)Fails with the errorFails with the error, nothing is queued

Queued writes#

A queued write is retried until it goes through.

A queued write can still be refused when it is finally sent, for example when your rules changed in the meantime. The SDK then drops the write and puts the local copy back to the server's version: a refused new document disappears, and a refused update or delete shows the server's document again. Listeners see the change. Your app is told through a callback, with the document id, the operation (create, update or delete) and the server's error:

PlatformCallback
FlutterAbrovaSync.documentRejections (stream), or the AbrovaSync.onDocumentRejected setter
React NativeAbrovaSync.onDocumentRejected(callback), returns a function that removes it
WebabrovaSync.onDocumentRejected(callback), returns a function that removes it
AndroidSyncListener.onDocumentRejected(documentId, operation, error), added with addSyncListener()
iOSSyncEngine.SyncListener.onDocumentRejected(documentId:operation:error:), added with addSyncListener(_:)

The maxSyncRetries setting is deprecated on every SDK and has no effect.

Flutter#

dart
// Enable offline persistence in initialization
await AbrovaSync.init(AbrovaSyncConfig(
  apiKey: 'YOUR_API_KEY',
  offlineEnabled: true,
));

// Sync state: idle, syncing, offline or error
final syncStateSub = AbrovaSync.syncStateChanges.listen((state) {
  print('Sync state: $state');
});

// How many writes are waiting to be sent
final pendingSub = AbrovaSync.pendingCountChanges.listen((count) {
  print('Pending writes: $count');
});

// Queued writes the server refused when they were sent
final rejectionSub = AbrovaSync.documentRejections.listen((rejection) {
  print('${rejection.operation} of ${rejection.documentId} refused '
      '(${rejection.error.statusCode}): ${rejection.error.message}');
});

// All CRUD operations work offline automatically!
final doc = await collection.add({'task': 'Buy milk'});
// Offline: saved locally and returned at once, synced when back online

// Force sync when needed
await AbrovaSync.forceSyncNow();

// When the screen closes:
syncStateSub.cancel();
pendingSub.cancel();
rejectionSub.cancel();

Any number of widgets can listen to these streams. The onSyncState, onPendingCount and onDocumentRejected setters still work, but each keeps one callback, so a second screen replaces the first one's.

React Native#

typescript
// Enable offline persistence in initialization
await AbrovaSync.init({
  apiKey: 'YOUR_API_KEY',
  offlineEnabled: true,
});

// Sync state: 'idle' | 'syncing' | 'offline' | 'error'
const stopSyncState = AbrovaSync.onSyncState((state) => {
  console.log('Sync state:', state);
});

// How many writes are waiting to be sent
const stopPending = AbrovaSync.onPendingCount((count) => {
  console.log('Pending writes:', count);
});

// Queued writes the server refused when they were sent
const stopRejected = AbrovaSync.onDocumentRejected((documentId, operation, error) => {
  console.warn(`${operation} of ${documentId} refused (${error.statusCode}): ${error.message}`);
});

// All CRUD operations work offline automatically!
const doc = await collection.add({ task: 'Buy milk' });
// Offline: saved locally and returned at once, synced when back online

// Force sync when needed
await AbrovaSync.forceSyncNow();

Web#

typescript
// Offline persistence (IndexedDB) is on by default on Web
const abrovaSync = new AbrovaSync({
  apiKey: 'YOUR_API_KEY',
});

// Listen to sync state: 'idle' | 'syncing' | 'offline' | 'error'
abrovaSync.onSyncState((state) => {
  console.log('Sync state:', state);
});

// Listen to connection state: 'connecting' | 'connected' | 'disconnected' | 'error'
abrovaSync.onConnectionState((state) => {
  console.log('Connection:', state);
});

// Monitor pending changes
abrovaSync.onPendingCount((count) => {
  document.getElementById('pending-badge').textContent = String(count);
});

// Queued writes the server refused when they were sent
abrovaSync.onDocumentRejected((documentId, operation, error) => {
  console.warn(`${operation} of ${documentId} refused (${error.statusCode}): ${error.message}`);
});

// All operations work offline automatically
const doc = await collection.add({ task: 'Buy milk' });

// Force sync
await abrovaSync.forceSyncNow();

After disconnect() (or goOffline()), writes are queued and reads come from the cache until you call reconnect() (or goOnline()).

Android#

kotlin
// Enable offline persistence in configuration
val config = AbrovaSyncConfig.Builder("YOUR_API_KEY")
    .offlineEnabled(true)
    .build()

val abrovaSync = AbrovaSync.initialize(context, config)

// Observe sync state with Flow
lifecycleScope.launch {
    abrovaSync.getSyncState()?.collect { state ->
        when (state) {
            SyncState.IDLE -> Log.d("AbrovaSync", "All synced")
            SyncState.SYNCING -> Log.d("AbrovaSync", "Syncing...")
            SyncState.OFFLINE -> Log.d("AbrovaSync", "Working offline")
            SyncState.ERROR -> Log.d("AbrovaSync", "Sync error")
        }
    }
}

// Queued writes the server refused when they were sent
abrovaSync.addSyncListener(object : SyncListener {
    override fun onSyncStarted() {}
    override fun onSyncCompleted(syncedCount: Int) {}
    override fun onSyncFailed(error: Throwable) {}
    override fun onConflictDetected(conflict: ConflictInfo) {}
    override fun onDocumentSynced(documentId: String, operation: OperationType) {}
    override fun onDocumentRejected(documentId: String, operation: OperationType, error: Throwable) {
        val status = (error as? AbrovaSyncException.NetworkException)?.statusCode
        Log.w("AbrovaSync", "$operation of $documentId refused ($status): ${error.message}")
    }
})

// All CRUD operations work offline automatically!
val doc = collection.add(mapOf("task" to "Buy milk"))
// Offline: saved locally and returned at once, synced when back online

// Force sync when needed
abrovaSync.forceSyncNow()

iOS#

swift
// Enable offline persistence in configuration
let config = AbrovaSyncConfigBuilder(apiKey: "YOUR_API_KEY")
    .offlineEnabled(true)
    .build()

AbrovaSync.initialize(config: config)

// Observe sync state with Combine
AbrovaSync.shared.syncStatePublisher?
    .receive(on: DispatchQueue.main)
    .sink { state in
        switch state {
        case .idle: print("All synced")
        case .syncing: print("Syncing...")
        case .offline: print("Working offline")
        case .error: print("Sync error")
        }
    }
    .store(in: &cancellables)

// Queued writes the server refused when they were sent.
// The other SyncListener methods are optional.
final class RejectedWrites: SyncEngine.SyncListener {
    func onDocumentRejected(documentId: String, operation: OperationType, error: Error) {
        let status = (error as? AbrovaSyncError)?.statusCode
        print("\(operation) of \(documentId) refused (\(String(describing: status))): \(error.localizedDescription)")
    }
}
let rejectedWrites = RejectedWrites() // keep a reference
AbrovaSync.shared.addSyncListener(rejectedWrites)

// All CRUD operations work offline automatically!
let doc = try await collection.add(data: ["task": "Buy milk"])
// Offline: saved locally and returned at once, synced when back online

// Force sync when needed
AbrovaSync.shared.forceSyncNow()

Optional settings#

Two settings change how offline sync behaves. Both are optional; the defaults suit most apps.

SettingDefaultEffect
syncOnReconnecttrueSend queued writes automatically when the connection comes back. Set it to false to send them only when you call forceSyncNow().
conflictStrategyserverWinsWhich version wins when a document changed both on the device and on the server. See Conflict Resolution.
dart
// Flutter
await AbrovaSync.init(AbrovaSyncConfig(
  apiKey: 'YOUR_API_KEY',
  offlineEnabled: true,
  syncOnReconnect: false,
  conflictStrategy: ConflictStrategy.clientWins,
));
typescript
// React Native
await AbrovaSync.init({
  apiKey: 'YOUR_API_KEY',
  offlineEnabled: true,
  syncOnReconnect: false,
  conflictStrategy: 'clientWins',
});

// Web
const abrovaSync = new AbrovaSync({
  apiKey: 'YOUR_API_KEY',
  syncOnReconnect: false,
  conflictStrategy: 'clientWins',
});
kotlin
// Android
val config = AbrovaSyncConfig.Builder("YOUR_API_KEY")
    .offlineEnabled(true)
    .syncOnReconnect(false)
    .conflictStrategy(ConflictStrategy.CLIENT_WINS)
    .build()
swift
// iOS
let config = AbrovaSyncConfigBuilder(apiKey: "YOUR_API_KEY")
    .offlineEnabled(true)
    .syncOnReconnect(false)
    .conflictStrategy(.clientWins)
    .build()
Server-side note.

The Node.js Admin SDK is designed for server-side use where offline persistence is not applicable. For client-side Node.js apps (Electron), consider using the Web SDK. For REST API clients, implement your own caching layer using localStorage, IndexedDB, or SQLite. Queue write operations when offline and sync when the connection is restored.

این صفحه مفید بود؟
گزارش مشکل