Offline-first synchronization plugin for NativePHP Mobile applications.
Stop fighting with offline data. This plugin handles queuing, sync, and conflicts so you can focus on building features. Works out-of-the-box with zero native code required.
- ✅ Automatic Queue Management - Operations are automatically queued when offline
- ✅ Bidirectional Sync - Push local changes and pull remote updates
- ✅ 4 Conflict Resolution Strategies - Server wins, Client wins, Last write wins, Merge
- ✅ Auto-Connectivity Monitoring - Syncs automatically when connection returns
- ✅ Background Sync - Works even when app is closed (iOS/Android)
- ✅ Secure by Default - HTTPS enforcement, auth-agnostic design (your app controls auth)
- ✅ Observable - Laravel events, logs, Artisan commands
- ✅ Zero Native Code - All native bridges included (Kotlin + Swift)
- PHP ≥ 8.1
- Laravel ≥ 10.0
- NativePHP ≥ 0.8.0
- iOS ≥ 14.0
- Android API Level ≥ 24 (Android 7.0)
composer require techparse/offline-syncphp artisan native:plugin:register techparse/offline-syncphp artisan vendor:publish --tag=offline-sync-configphp artisan migrateEdit .env:
SYNC_API_URL=https://api.yourapp.com
SYNC_REQUIRE_HTTPS=trueuse Techparse\OfflineSync\Traits\Syncable;
class Task extends Model
{
use Syncable;
// Optional: customize sync behavior
protected $syncResourceName = 'tasks';
protected $syncExcluded = ['internal_notes'];
}Edit config/offline-sync.php:
'resource_mapping' => [
'tasks' => \App\Models\Task::class,
'users' => \App\Models\User::class,
],// Operations are automatically queued when offline
$task = Task::create(['title' => 'My Task']);
// Manual sync (optional)
use Techparse\OfflineSync\Facades\OfflineSync;
OfflineSync::sync(['tasks']);
// Check status
$status = OfflineSync::getStatus();
// ['pending_count' => 5, 'is_syncing' => false, 'last_sync' => '...']With the Syncable trait, all create/update/delete operations are automatically queued:
// These are automatically queued when offline
$task = Task::create(['title' => 'New Task']);
$task->update(['completed' => true]);
$task->delete();use Techparse\OfflineSync\Facades\OfflineSync;
// Bidirectional sync (push + pull)
OfflineSync::sync(['tasks', 'users']);
// Push only (local → server)
OfflineSync::push(['tasks']);
// Pull only (server → local)
OfflineSync::pull(['users']);// Get pending items
$pending = OfflineSync::getPending();
$pendingTasks = OfflineSync::getPending('tasks');
// Purge old synced items (older than 7 days)
OfflineSync::purgeOldItems(7);# Push local changes to server
php artisan sync:push
# Push specific resources
php artisan sync:push tasks users
# Pull remote changes
php artisan sync:pull tasks users
# Check queue status
php artisan sync:status
# Clear queue
php artisan sync:clear
php artisan sync:clear --failedConfigure in config/offline-sync.php:
'conflict_resolution' => [
// Default strategy for all resources
'default_strategy' => 'server_wins',
// Per-resource strategies
'per_resource' => [
'tasks' => 'last_write_wins',
'users' => 'server_wins',
],
],| Strategy | Description | Best For |
|---|---|---|
| server_wins | Server data always overwrites local | Critical data, auth |
| client_wins | Local data always overwrites server | User preferences |
| last_write_wins | Newest timestamp wins | Most use cases |
| merge | Intelligent field-level merge | Complex data |
The plugin is auth-agnostic — it does not manage tokens or credentials. Your application is responsible for authentication. To forward an auth header on every sync request, set offline-sync.security.headers at runtime (e.g. in your AppServiceProvider):
// app/Providers/AppServiceProvider.php
public function boot(): void
{
$token = $this->app->make(Request::class)->bearerToken();
if ($token) {
config(['offline-sync.security.headers' => [
'Authorization' => 'Bearer ' . $token,
]]);
}
}This works with any auth system: Laravel Sanctum, Passport, API keys, etc.
SYNC_REQUIRE_HTTPS=trueAdd to routes/api.php:
use Techparse\OfflineSync\Http\Controllers\SyncController;
Route::middleware('auth:sanctum')->prefix('sync')->group(function () {
Route::post('/push', [SyncController::class, 'push']);
Route::get('/pull/{resource}', [SyncController::class, 'pull']);
Route::get('/status', [SyncController::class, 'status']);
Route::get('/ping', [SyncController::class, 'ping']);
});Use the included SyncController or extend it for custom logic.
Listen to sync events in your application:
use Techparse\OfflineSync\Events\SyncCompleted;
Event::listen(SyncCompleted::class, function ($event) {
Log::info("Synced {$event->synced} items in {$event->durationMs}ms");
});SyncStarted- Sync process startedSyncCompleted- Sync finished successfullySyncFailed- Sync failedItemQueued- Item added to queueItemSynced- Item synchronizedConflictDetected- Conflict detectedQueuePurged- Old items purged
See config/offline-sync.php for all options:
- API URL and security headers
- Resource mapping
- Conflict resolution strategies
- Connectivity settings
- Performance tuning
- Security options
- Logging configuration
- Automatic connectivity monitoring
- Background sync with WorkManager
- WiFi-only mode support
- Battery-aware scheduling
- Network framework monitoring
- Background fetch
- App refresh scheduling
- Low power mode respect
All native code is included. No manual native development required.
Run the test suite:
composer testOr with coverage:
composer test-coverage- Email: offlinessync@techparse.fr
- Documentation: https://docs.offlinesync.techparse.fr
- Issues: https://github.com/Kromaric/offlinesync/issues
This software is open source, released under the MIT License. See LICENSE for details.
Built with ❤️ for the NativePHP community.
See CHANGELOG.md for version history.