____ ______ ______ __ _____ __ ______
/ __ \/ ____// ____// / / _/ | / // ____/
/ / / / /_ / /_ / / / / / |/ // __/
/ /_/ / __/ / __/ / /____/ / / /| // /___
\____/_/ /_/ /_____/___//_/ |_//_____/
ββββββββββββββββββ βββββββββββββββββ
ββββββββββββββββββββββββββββββββββββ
ββββββ βββββββββββββββββββ βββ
ββββββ βββββββββββββββββββ βββ
βββ ββββββ βββββββββββ βββ
βββ ββββββ βββββββββββ βββ
Work Offline. Sync Later.
The most comprehensive offline-first framework for iOS. Production-ready with CRDT support, real encryption, delta sync, and multi-device collaboration.
Features β’ Installation β’ Quick Start β’ Architecture β’ Docs
- CRDT Support - Conflict-free replicated data types for automatic conflict resolution
- Real Encryption - AES-256-GCM & ChaCha20-Poly1305 with secure key management
- Delta Sync - Only sync what changed, save bandwidth
- Multi-Device Sync - Real-time collaboration across all your devices
- Bandwidth Optimization - Adaptive chunking and smart queue management
- Background Sync - BGTaskScheduler integration for reliable background updates
- Optimistic UI - Instant UI updates with automatic rollback on failure
- Features
- Installation
- Quick Start
- Architecture
- Core Components
- Advanced Features
- Documentation
- Contributing
- License
Users expect apps to work everywhere β in subways, airplanes, rural areas, or during network outages. Offline-first isn't just a feature; it's a user expectation.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β "The best user experience is one that works, period." β
β β Every User Ever β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
| Feature | Description | Status |
|---|---|---|
| πΎ Local Storage | Encrypted file-based persistence | β |
| π Auto Sync | Background synchronization with BGTaskScheduler | β |
| βοΈ Conflict Resolution | CRDT-based automatic resolution | β |
| π Retry Queue | Exponential backoff with persistence | β |
| π‘ Network Monitor | Real-time connectivity & quality detection | β |
| π Encryption | AES-256-GCM at-rest encryption | β |
| ποΈ Compression | LZ4/ZLIB/LZMA/LZFSE compression | β |
| π Delta Sync | Efficient change-only synchronization | β |
| π± Multi-Device | Real-time cross-device sync | β |
| β‘ Optimistic UI | Instant updates with auto-rollback | β |
| πΆ Bandwidth Optimizer | Adaptive transfer management | β |
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Conflict Resolution β
βββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββββββ€
β Last Write β Most recent timestamp wins β
βββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββ€
β Server Wins β Remote data always takes precedence β
βββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββ€
β Client Wins β Local changes always take precedence β
βββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββ€
β Custom Merge β Your logic decides the outcome β
βββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββ€
β CRDT β Automatic conflict-free merging β
βββββββββββββββββββ΄ββββββββββββββββββββββββββββββββββββββββββββ
graph TD
A[π€ User Action] --> B{π‘ Network?}
B -->|Online| C[π Remote API]
B -->|Offline| D[πΎ Local Store]
C --> E[β¬οΈ Sync to Local]
D --> F[π Queue Operation]
F --> G{π‘ Back Online?}
G -->|Yes| H[β¬οΈ Process Queue]
H --> I[π Delta Sync]
I --> C
E --> J[β
Data Available]
D --> J
style A fill:#4CAF50,color:#fff
style B fill:#FF9800,color:#fff
style C fill:#2196F3,color:#fff
style D fill:#9C27B0,color:#fff
style J fill:#4CAF50,color:#fff
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β OfflineFirst Framework β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β βββββββββββββββ βββββββββββββββ βββββββββββββββ βββββββββββββββ β
β β CRDT β β Encryption β β Compression β β Delta Sync β β
β β Engine β β Engine β β Engine β β Engine β β
β βββββββββββββββ βββββββββββββββ βββββββββββββββ βββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β βββββββββββββββ βββββββββββββββ βββββββββββββββ βββββββββββββββ β
β β Retry β β Background β β Bandwidth β β Multi-Deviceβ β
β β Queue β β Sync β β Optimizer β β Sync β β
β βββββββββββββββ βββββββββββββββ βββββββββββββββ βββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β βββββββββββββββ βββββββββββββββ βββββββββββββββ βββββββββββββββ β
β β Optimistic β β Network β β Storage β β Analytics β β
β β UI β β Monitor β β Manager β β Manager β β
β βββββββββββββββ βββββββββββββββ βββββββββββββββ βββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
dependencies: [
.package(
url: "https://github.com/Nahodan/iOS-Offline-First-Framework.git",
from: "2.0.0"
)
]Then import:
import OfflineFirstFrameworkimport OfflineFirstFramework
// Initialize with default configuration
let offlineManager = OfflineFirstManager.shared
offlineManager.initialize(with: OfflineFirstConfiguration())struct Task: Syncable {
let id: String
var title: String
var isCompleted: Bool
var lastModified: Date
var version: Int64
var isDirty: Bool
var syncId: String { id }
}let task = Task(
id: UUID().uuidString,
title: "Buy groceries",
isCompleted: false,
lastModified: Date(),
version: 1,
isDirty: true
)
// Saves locally, queues for sync if offline
offlineManager.save(task)
.subscribe(onNext: { result in
print("Saved: \(result)")
})
.disposed(by: disposeBag)offlineManager.syncStatus
.subscribe(onNext: { status in
switch status {
case .idle:
print("Ready to sync")
case .inProgress:
print("Syncing...")
case .completed:
print("Sync complete!")
case .failed(let error):
print("Sync failed: \(error)")
}
})
.disposed(by: disposeBag)Conflict-free Replicated Data Types for automatic merge:
// Last-Writer-Wins Register
var counter = LWWRegister(value: "Hello", nodeId: deviceId)
counter = counter.update("World")
// OR-Set (add/remove support)
var set = ORSet<String>(nodeId: deviceId)
set = set.add("item1")
set = set.remove("item1")
// PN-Counter (increment/decrement)
var visits = PNCounter(nodeId: deviceId)
visits = visits.increment()
visits = visits.decrement()
// Merge with remote
let merged = counter.merge(with: remoteCounter)Production-ready encryption with CryptoKit:
let encryption = EncryptionEngine()
// Encrypt with master key
let encrypted = try encryption.encrypt(data)
// Decrypt
let decrypted = try encryption.decrypt(encrypted)
// Password-based encryption
let encryptedWithPassword = try encryption.encrypt(data, password: "secret")Multiple algorithms with adaptive selection:
let compression = CompressionEngine(configuration: .default)
// Compress
let compressed = try compression.compress(data)
print("Ratio: \(compressed.compressionRatio)")
// Decompress
let original = try compression.decompress(compressed)Only sync what changed:
let deltaEngine = DeltaSyncEngine()
// Detect changes
let change = try await deltaEngine.detectChanges(
old: oldTask,
new: newTask,
entityType: "Task"
)
// Apply patch
let patched = try await deltaEngine.applyPatch(change.patch!, to: oldTask)Persistent queue with exponential backoff:
let queue = RetryQueue(configuration: .default)
// Queue operation
let operation = OperationBuilder()
.type(.update)
.entity(id: "123", type: "Task")
.payload(try JSONEncoder().encode(task))
.priority(.high)
.build()
await queue.enqueue(operation)
// Monitor progress
queue.statusPublisher
.sink { status in
print("Pending: \(status.pendingCount)")
}
.store(in: &cancellables)Reliable background updates with BGTaskScheduler:
// In AppDelegate
let scheduler = BackgroundSyncScheduler()
scheduler.registerBackgroundTask()
scheduler.setSyncHandler(DefaultSyncHandler(retryQueue: queue))
scheduler.enable()
// Check last sync
if let result = scheduler.lastSyncResult {
print("Last sync: \(result.itemsSynced) items")
}Instant updates with auto-rollback:
let store = ObservableEntityStore<Task>()
let optimistic = OptimisticUpdateManager(store: store)
// Update with instant UI feedback
optimistic.optimisticUpdate(updatedTask) {
try await api.update(updatedTask)
}
// SwiftUI support
TaskRow(task: task)
.pendingOverlay(optimistic.isPending(entityId: task.id))Real-time collaboration:
let multiDevice = MultiDeviceSyncManager(
configuration: .default(deviceName: UIDevice.current.name)
)
multiDevice.start()
// Send changes to all devices
try await multiDevice.sendDelta(changes)
// Handle incoming changes
multiDevice.setMessageHandler { message in
// Process sync message
}
// Monitor connected devices
multiDevice.devicesPublisher
.sink { devices in
print("Connected: \(devices.count) devices")
}
.store(in: &cancellables)Smart transfer management:
let bandwidth = BandwidthOptimizer(configuration: .default)
// Queue transfer
let taskId = await bandwidth.queueTransfer(
TransferTask(type: .upload, dataSize: fileSize, priority: .normal)
)
// Monitor network quality
bandwidth.qualityPublisher
.sink { quality in
if quality.isPoor {
print("Poor connection - deferring large syncs")
}
}
.store(in: &cancellables)
// Check if should defer
if await bandwidth.shouldDeferSync(priority: .low) {
// Wait for better connection
}let engine = ConflictResolutionManager()
// Detect conflicts
let conflicts = engine.detectConflicts(local: localTask, remote: remoteTask)
// Custom resolution
engine.autoResolveConflicts(conflicts, strategy: .custom { local, remote in
// Your merge logic
var merged = remote
merged.localNotes = local.localNotes
return merged
})let networkMonitor = NetworkStateManager()
networkMonitor.isOnline
.distinctUntilChanged()
.subscribe(onNext: { isOnline in
if isOnline {
syncEngine.syncNow()
}
})
.disposed(by: disposeBag)| Requirement | Version |
|---|---|
| iOS | 15.0+ |
| macOS | 12.0+ |
| tvOS | 15.0+ |
| watchOS | 8.0+ |
| Xcode | 15.0+ |
| Swift | 5.9+ |
Detailed documentation available in Documentation/:
Contributions are welcome! Please read the Contributing Guide first.
- Fork the repo
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'feat: add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License β see the LICENSE file for details.
Muhittin Camdali β @Nahodan
Built with β€οΈ for apps that work everywhere