Offline-First Nedir ve Neden Onemlidir
Mobil uygulamalarin buyuk cogunlugu internet baglantisi varsayimiyla tasarlanir. Ancak gercek dunya kosullarinda kullanicilar metro, asansor, kirsal alan veya yogun kalabalik gibi baglantinin zayif ya da tamamen kesildig ortamlarda uygulama kullanmaya devam etmek ister. Offline-first yaklasimi, uygulamanin once yerel ortamda calismasini saglayip, baglanti mevcut oldugunda sunucuyla senkronize olmasini esas alir.
Bu felsefe sadece bir teknik tercih degil, ayni zamanda kullanici deneyimi acisindan kritik bir karardi. Offline-first uygulamalar daha hizli acilir, daha guvenilir calisir ve kullaniciya kesintisiz bir deneyim sunar.
Offline-First Tasarim Felsefesi
Offline-first mimarinin temelinde uc prensip vardir:
- Local-first veri erisimi: Tum okuma islemleri once yerel veritabanindan karsilanir.
- Iyimser guncellemeler: Kullanici aksiyonlari aninda yerel olarak uygulanir, sunucu senkronizasyonu arka planda gerceklesir.
- Catisma cozumu: Ayni veri uzerinde birden fazla degisiklik oldugunda tutarli bir birlestirme stratejisi uygulanir.
Bu prensipleri uygularken asagidaki mimari katmanlari dikkate almak gerekir:
| Katman | Sorumluluk | Ornek Teknoloji |
|---|---|---|
| UI Katmani | Kullanici etkilesimi, durum gosterimi | React Native, Flutter |
| Yerel Veri Katmani | Kalici depolama, sorgu isleme | SQLite, Realm, WatermelonDB |
| Senkronizasyon Katmani | Cakisma cozumu, delta hesaplama | Ozel sync engine, PowerSync |
| Ag Katmani | API iletisimi, kuyruk yonetimi | Axios, Dio, WorkManager |
Local-First Veri Yonetimi
SQLite
SQLite, mobil platformlarda en yaygin kullanilan gomulu veritabanidir. Hem iOS hem Android'de native destek sunar. React Native icin react-native-sqlite-storage, Flutter icin sqflite veya drift kutuphaneleri tercih edilir.
// Flutter - Drift ile yerel tablo tanimi
class Tasks extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get title => text().withLength(min: 1, max: 200)();
BoolColumn get completed => boolean().withDefault(const Constant(false))();
IntColumn get syncStatus => integer().withDefault(const Constant(0))();
DateTimeColumn get updatedAt => dateTime()();
}
syncStatus alani her kaydin senkronizasyon durumunu takip eder: 0 (senkron), 1 (beklemede), 2 (catismali).
Realm
Realm, nesne tabanli bir veritabani olup ozellikle karmasik iliskisel veriler icin SQLite'a alternatif sunar. Otomatik senkronizasyon ozelligi MongoDB Atlas Device Sync ile birlikte gelir.
WatermelonDB
React Native projelerinde yuksek performansli offline-first uygulamalar icin WatermelonDB one cikar. Lazy loading ve observable sorgu destegi sayesinde buyuk veri kumeleriyle bile akici bir deneyim sunar.
// WatermelonDB model tanimi
class Task extends Model {
static table = 'tasks';
static associations = {
projects: { type: 'belongs_to', key: 'project_id' },
};
@text('title') title;
@field('completed') completed;
@field('sync_status') syncStatus;
@date('updated_at') updatedAt;
}
Veritabani Secim Kriterleri
| Kriter | SQLite | Realm | WatermelonDB |
|---|---|---|---|
| Platform Destegi | iOS, Android, Web | iOS, Android | React Native |
| Performans (10K+ kayit) | Iyi | Cok Iyi | Cok Iyi |
| Otomatik Sync | Yok | MongoDB Atlas | Ozel gerektirir |
| Ogrenme Egrisi | Dusuk | Orta | Orta |
| Topluluk Destegi | Cok Genis | Genis | Orta |
Conflict Resolution Stratejileri
Offline ortamda birden fazla cihaz ayni veriyi degistirdiginde catismalar kacilmazdir. Dogru strateji secimi uygulamanin veri butunlugunu dogrudan etkiler.
Last-Write-Wins (LWW)
En basit yaklasimdir. Her degisiklik bir zaman damgasi tasir ve en son yapilan degisiklik kazanir.
function resolveConflict(localRecord, serverRecord) {
if (localRecord.updatedAt > serverRecord.updatedAt) {
return { winner: 'local', record: localRecord };
}
return { winner: 'server', record: serverRecord };
}
Avantaji basitligidir; dezavantaji ise veri kaybina yol acabilmesidir. Kullanicinin yaptigi bir degisiklik sessizce kaybolabilir.
CRDT (Conflict-free Replicated Data Types)
CRDT'ler matematiksel olarak catismasiz birlestirme garantisi verir. Birden fazla kopya bagimsiz olarak guncellenebilir ve sonucta tutarli bir duruma yakinsar.
Yaygin CRDT tipleri:
- G-Counter: Sadece artan sayaclar icin (begeni sayisi gibi)
- LWW-Register: Tekil deger icin son yazan kazanir
- OR-Set: Eleman ekleme ve cikarma destekli kume
- RGA (Replicated Growable Array): Siralama gerektiren listeler icin
// Basitlesilmis LWW-Register implementasyonu
interface LWWRegister<T> {
value: T;
timestamp: number;
nodeId: string;
}
function mergeLWW<T>(local: LWWRegister<T>, remote: LWWRegister<T>): LWWRegister<T> {
if (local.timestamp > remote.timestamp) return local;
if (remote.timestamp > local.timestamp) return remote;
// Ayni zaman damgasinda node ID ile deterministik secim
return local.nodeId > remote.nodeId ? local : remote;
}
Alan Bazli Birlestirme
Kayit duzeyinde degil, alan duzeyinde catisma cozumu yapmak veri kaybini minimize eder. Ornegin bir kullanici baslik alanini, digeri aciklama alanini guncellediginde her iki degisiklik de korunur.
Data Sync Patterns
Background Sync
Uygulama arka planda veya agla yeniden baglanti kuruldugunda senkronizasyonu tetikler. Android'de WorkManager, iOS'te BGTaskScheduler kullanilir.
// Android WorkManager ile periyodik sync
val syncRequest = PeriodicWorkRequestBuilder<DataSyncWorker>(
repeatInterval = 15,
repeatIntervalTimeUnit = TimeUnit.MINUTES
).setConstraints(
Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
).build()
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
"data_sync",
ExistingPeriodicWorkPolicy.KEEP,
syncRequest
)
Delta Sync
Tam veri seti yerine yalnizca degisen kayitlari gonderir. Bu yaklasim bant genisligini onemli olcude azaltir.
// Delta sync - sadece degisen kayitlari gonder
async function deltaSync(lastSyncTimestamp) {
const pendingChanges = await db.query(
'SELECT * FROM tasks WHERE sync_status = 1 AND updated_at > ?',
[lastSyncTimestamp]
);
const response = await api.post('/sync', {
changes: pendingChanges,
lastSync: lastSyncTimestamp,
});
// Sunucudan gelen degisiklikleri yerel veritabanina uygula
for (const change of response.serverChanges) {
await applyServerChange(change);
}
return response.newSyncTimestamp;
}
Sync Durum Yonetimi
Senkronizasyon sureci kullaniciya seffaf olmalidir. Asagidaki durum makinesi yaklasimi tercih edilir:
| Durum | Aciklama | UI Gostergesi |
|---|---|---|
| SYNCED | Tum veriler guncel | Yesil onay ikonu |
| PENDING | Yerel degisiklikler bekliyor | Sari bekleme ikonu |
| SYNCING | Aktif senkronizasyon devam ediyor | Donen animasyon |
| CONFLICT | Manuel cozum gerektiren catisma | Kirmizi uyari |
| ERROR | Senkronizasyon hatasi | Hata mesaji |
Push Notification Mimarisi
Push bildirimler, kullanicilari uygulamayla yeniden etkilesime gecirmek icin en etkili araclardan biridir. Dogru uygulandiginda kullanici bagliligini yuzde 88'e kadar artirabilir.
APNs ve FCM Altyapisi
Apple Push Notification Service (APNs) ve Firebase Cloud Messaging (FCM), sirasiyla iOS ve Android platformlarinin push bildirim altyapilaridir.
// Node.js - FCM ile push bildirim gonderimi
const admin = require('firebase-admin');
async function sendPushNotification(token, payload) {
const message = {
token: token,
notification: {
title: payload.title,
body: payload.body,
},
data: {
type: payload.type,
referenceId: payload.referenceId,
},
android: {
priority: 'high',
notification: {
channelId: 'important_updates',
clickAction: 'OPEN_DETAIL',
},
},
apns: {
payload: {
aps: {
sound: 'default',
badge: payload.badgeCount,
'mutable-content': 1,
},
},
},
};
return admin.messaging().send(message);
}
Rich Notifications
Zengin bildirimler, metin disinda gorsel, video ve ozel arayuz ogeleri icerebilir. iOS'te Notification Service Extension, Android'de BigPictureStyle ile uygulanir.
// iOS - Notification Service Extension ile rich notification
class NotificationService: UNNotificationServiceExtension {
override func didReceive(_ request: UNNotificationRequest,
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
guard let mutableContent = request.content.mutableCopy() as? UNMutableNotificationContent,
let imageURL = mutableContent.userInfo["image_url"] as? String else {
contentHandler(request.content)
return
}
downloadImage(from: imageURL) { attachment in
if let attachment = attachment {
mutableContent.attachments = [attachment]
}
contentHandler(mutableContent)
}
}
}
Actionable Notifications
Kullanicinin bildirimi acmadan dogrudan aksiyon almasini saglayan butonlar eklenebilir. Ornegin bir siparis bildiriminde "Onayla" ve "Reddet" butonlari sunulabilir.
// Android - Actionable notification
val approveIntent = Intent(context, NotificationActionReceiver::class.java).apply {
action = "ACTION_APPROVE"
putExtra("order_id", orderId)
}
val notification = NotificationCompat.Builder(context, "orders")
.setContentTitle("Yeni Siparis")
.setContentText("Siparis #$orderId onayinizi bekliyor")
.addAction(R.drawable.ic_check, "Onayla",
PendingIntent.getBroadcast(context, 0, approveIntent, PendingIntent.FLAG_IMMUTABLE))
.addAction(R.drawable.ic_close, "Reddet",
PendingIntent.getBroadcast(context, 1, rejectIntent, PendingIntent.FLAG_IMMUTABLE))
.build()
Notification Segmentation ve Personalization
Toplu bildirim gondermek kullanici memnuniyetsizligine ve uygulama silmelerine yol acar. Segmentasyon ve kisisellistirme, bildirim stratejisinin temel taslarindan biridir.
Segmentasyon Kriterleri
| Kriter | Ornek | Kullanim Alani |
|---|---|---|
| Davranissal | Son 7 gunde aktif | Re-engagement |
| Demografik | Sehir, dil, yas grubu | Lokasyon bazli teklifler |
| Transaksiyonel | Sepette urun birakti | Terk edilen sepet hatirlatmasi |
| Tercih bazli | Bildirim kategorisi secimi | Kullanici kontrollu bildirimler |
Kisisellistirme Stratejisi
Smart Maple olarak Ankara merkezli projelerimizde, push bildirim stratejisini kullanici davranisi analizine dayandiriyoruz. Her kullanicinin uygulama ici aksiyonlarina gore bildirim icerigi, zamanlama ve sikligi optimize edilir.
Etkili kisisellistirme icin su veriler degerlendirilir:
- Kullanicinin en aktif oldugu saat dilimleri
- En cok etkilesimde bulundugu icerik turleri
- Bildirim acma oranlari ve gecmis tepkileri
- Uygulama ici satin alma veya donusum gecmisi
Offline UX Best Practices
Offline deneyim tasarimi, kullanicinin baglanti durumunu anlamasi ve guvenle islem yapmaya devam etmesi icin kritik oneme sahiptir.
Baglanti Durumu Gostergesi
Kullaniciya baglanti durumu hakkinda net bilgi verilmelidir. Ancak bu bilgi dikkat dagitmamali, sadece gerektiginde gorunur olmalidir. Surekli gozuken bir banner yerine, kullanici bir islem yaptiginda durumun bildirildigi yaklasim tercih edilir.
Iyimser UI Guncellemeleri
Kullanici bir islem yaptiginda sonuc aninda yansitilmali, senkronizasyon arka planda gerceklesmelidir. Ornegin bir gorev tamamlandiginda:
- Yerel veritabaninda kayit aninda guncellenir
- UI aninda yeni durumu yansitir
- Senkronizasyon kuyruguna eklenir
- Baglanti varsa arka planda sunucuya gonderilir
- Hata durumunda kullaniciya geri bildirim verilir
Offline Erisim Sinirlarini Belirleme
Her islem offline yapilabilir olmak zorunda degildir. Odeme islemi gibi gercek zamanli dogrulama gerektiren islemler icin kullaniciya net bir aciklama sunulmalidir.
React Native ve Flutter'da Offline-First Yaklasim
React Native Ekosistemi
React Native'de offline-first uygulama gelistirmek icin su kutuphaneler yaygin olarak kullanilir:
- WatermelonDB: Yerel veritabani ve reactive sorgular
- NetInfo: Ag durumu izleme
- AsyncStorage / MMKV: Hafif anahtar-deger depolama
- react-native-background-fetch: Arka plan senkronizasyon tetikleme
// React Native - Ag durumuna gore sync tetikleme
import NetInfo from '@react-native-community/netinfo';
import { syncEngine } from './sync';
NetInfo.addEventListener(state => {
if (state.isConnected && state.isInternetReachable) {
syncEngine.startSync();
} else {
syncEngine.pauseSync();
}
});
Flutter Ekosistemi
Flutter'da offline-first yaklasim icin su paketler tercih edilir:
- drift (eski adi moor): Tip guvenli SQLite ORM
- hive: Hizli anahtar-deger deposu
- connectivity_plus: Ag durumu izleme
- workmanager: Arka plan gorev zamanlama
Smart Maple olarak Ankara merkezli projelerimizde, Flutter'in platform bagimsiz yapisini offline-first mimariyle birlestirerek tek kod tabaniyla iOS ve Android icin tutarli bir cevirim disi deneyim sunuyoruz.
// Flutter - Connectivity ile otomatik sync
class SyncManager {
late StreamSubscription<ConnectivityResult> _subscription;
void initialize() {
_subscription = Connectivity().onConnectivityChanged.listen((result) {
if (result != ConnectivityResult.none) {
_performDeltaSync();
}
});
}
Future<void> _performDeltaSync() async {
final pendingChanges = await _localDb.getPendingChanges();
if (pendingChanges.isEmpty) return;
try {
final response = await _apiClient.syncChanges(pendingChanges);
await _localDb.applyServerChanges(response.changes);
await _localDb.markAsSynced(pendingChanges);
} catch (e) {
// Sonraki sync denemesinde tekrar denenir
debugPrint('Sync basarisiz: $e');
}
}
}
Test ve Izleme Stratejisi
Offline-first uygulamalarin test edilmesi standart uygulamalara kiyasla daha karmasiktir. Asagidaki senaryolarin test edilmesi gerekir:
- Tamamen cevirim disi CRUD islemleri
- Baglanti geldiginde senkronizasyonun basariyla tamamlanmasi
- Ayni kaydin birden fazla cihazda degistirilmesi ve catisma cozumu
- Buyuk veri kumelerinde delta sync performansi
- Push bildirim teslim orani ve acilma orani izleme
- Arka plan sync gorevlerinin platform kisitlamalarina uygunlugu
Sonuc
Offline-first mobil uygulama gelistirme, kullanici deneyimini onemli olcude iyilestiren ancak dikkatli planlama gerektiren bir yaklasimdir. Dogru veritabani secimi, etkili catisma cozumu stratejisi ve guvenilir senkronizasyon mekanizmasi bu mimarinin uc temel ayagidir. Push bildirimler ise kullaniciyi uygulamaya geri getirmek icin vazgecilmez bir aractir; segmentasyon ve kisisellistirme ile etkinligi katlanarak artar.
Projenizin gereksinimlerini analiz ederek, basit senaryolar icin LWW, karmasik isbirligi gerektiren senaryolar icin CRDT tabanli cozumler degerlendirin. Hangi platformda gelistirirseniz gelistirin, offline deneyimi birinci sinif bir vatandas olarak ele almak, uygulamanizin guvenilirligini ve kullanici memnuniyetini dogrudan yukseltecektir.
Related Articles
MLOps Rehberi: Makine Öğrenmesi Modellerini Production'a Taşıma
Giriş: MLOps Nedir ve Neden Önemlidir? Makine öğrenmesi modelleri geliştirmek günümüzde nispeten kolaydır. Açık kaynak kütüphaneleri kullanarak son derece başarılı modeller oluşturabiliriz. Ancak bu modelleri production ortamına taşıyarak, ölçeklendirebilir, güvenilir ve sürdürülebilir şekilde çalıştırmak tamamen farklı bir hikayedir. Araştırmalara göre, veri bilimcileri tarafından geliştirilen makine öğrenmesi modellerinin %87'si hiçbir zaman production ortamına ulaşmaz. Bu başarısızlı
Read MoreLLM Fine-Tuning ve Özel Model Eğitimi Rehberi [2026]
LLM Fine-Tuning: Kurumsal Yapay Zeka Stratejisinin Temel Taşı Büyük dil modelleri (LLM), genel amaçlı metin üretimi ve anlama konusunda etkileyici performans sergiliyor. Ancak kurumsal ortamlarda belirli bir alan, terminoloji veya iş sürecine uyum sağlamaları gerektiğinde, genel bilgileri çoğu zaman yetersiz kalıyor. Bu noktada fine-tuning, yani ince ayar süreci devreye giriyor. Fine-tuning sayesinde mevcut bir temel modeli, kendi verileriniz ve ihtiyaçlarınız doğrultusunda özelleştirmek
Read MoreBilgisayarlı Görü Uygulamaları: Nesne Tespiti, OCR ve Endüstriyel AI
Bilgisayarlı Görü Uygulamaları: Endüstriyel ve Medikal AI'nin Temel Teknolojisi Bilgisayarlı görü, makine öğrenmesinin en etkili alanlarından biridir. Türkiye'de yaşanan dijital dönüşüm sürecinde, özellikle üretim, sağlık ve lojistik sektörlerinde görü tabanlı otomasyon kritik hale gelmiştir. Smart Maple olarak Ankara'da geliştirdiğimiz çözümler, son beş yılda 150+ kuruluşunun üretim verimliliğini ortalama %35 oranında artırmıştır. Bu rehberde, bilgisayarlı görü teknolojisinin iş değeri
Read More
