İnternet Yokken de Çalışması Gereken Uygulama
Bir uygulamanın en çok işe yarayacağı an, çoğu zaman internetin çalışmadığı an oluyor. Sağlam tasarımı ve Vakti Geçmeden'in QR doğrulaması üzerinden çevrimdışı öncelikli mimarinin kararları ve bedelleri.
Sağlam'ı tasarlarken kafamı en çok kurcalayan şey şuydu: uygulamanın işe yarayacağı an, tam da internetin çalışmayacağı an. Depremden sonraki ilk saatlerde şebeke ya düşük ya da tıkalı oluyor. Böyle bir uygulamaya sonradan “çevrimdışı modu” eklenmiyor — ya baştan öyle kuruluyor ya da hiç olmuyor.
Çevrimdışı bir özellik değil, bir varsayım
Çoğu uygulama şu varsayımla yazılıyor: internet var, yoksa da birazdan gelir. Önbellek bu varsayımın üstüne sonradan ekleniyor; sunucudan gelen cevabı saklıyoruz ki bir dahakine ekran hızlı açılsın. Bu, hız için iyi bir çözüm ama çevrimdışı çalışmak değil.
Çevrimdışı öncelikli mimaride varsayım tersine dönüyor: ekranı çizmek için gereken her şey zaten cihazda duruyor. Ağ, o verinin tazelenmesi için var; görünmesi için değil. Aradaki farkı tek bir soruyla ölçüyorum: uygulamayı hiç internet görmemiş bir cihaza kurup uçak modunda açsam kullanıcı ne görür?
Cevap boş ekran ya da dönen çember ise, o uygulama çevrimdışı çalışmıyor; sadece daha önce çalışmış olduğunu hatırlıyor. Aradaki fark, kullanıcının uygulamaya en çok ihtiyaç duyduğu anda ortaya çıkıyor.
Önbelleğe dayanan her çözüm ilk açılışta boştur. O an cihazda yalnızca iki şey vardır: uygulama paketinin içinde gönderdikleriniz ve daha önce bilinçli olarak indirttikleriniz. Çevrimdışı mimariyi bu iki kaynağın üstüne kurmak gerekiyor.
Cihazda durması gereken üç şey
Sağlam üzerinde çalışırken çevrimdışı gereksinimini üç parçaya ayırdım. Bu ayrım işi bir anda somutlaştırdı, çünkü üçünün de boyutu, tazelenme sıklığı ve kırılma biçimi farklı.
- Veri. Toplanma alanları, tahliye noktaları, bina ve zemin bilgisi. Nadiren değişiyor, boyutu kontrol edilebilir.
- Harita. Yol ağı, bina blokları, zemin katmanı. Ağırlığı bu taşıyor ve en çok burada tökezleniyor.
- Model. Risk skorunu ve tahliye rotasını üreten hesabın kendisi. En sık atlanan parça bu.
Üçüncüsünü ayrı yazmamın sebebi şu: veriyi ve haritayı cihaza indirip hesabı sunucuda yaparsanız hiçbir şey kazanmıyorsunuz. Kullanıcı “buradan nereye gitmeliyim” diye sorduğunda cevap yine ağdan geliyor. Skoru üreten mantığın da, rota hesabının da cihazda çalışması gerekiyor. Sunucuya kalan iş, katsayıları ve veri paketini güncellemek.
Haritayı tek dosyada taşımak
Harita tarafında alışılmış yöntem, karoları gezdikçe ağdan indirip önbellekte biriktirmek. Sıradan bir uygulamada makul; böyle bir senaryoda işe yaramıyor. Çünkü kullanıcının hiç gezmediği bölge, tam da acil durumda ihtiyaç duyacağı bölge olabiliyor. Önbellek geçmişe göre doluyor, ihtiyaca göre değil.
Bu yüzden tasarımda haritayı bir dosya olarak ele aldım. PMTiles, karoların tamamını tek bir dosyada tutuyor ve okuyucu dosyanın yalnızca ihtiyacı olan aralığını okuyor; MapLibre de bu dosyayı doğrudan kaynak olarak alıp çizebiliyor. Kazandırdığı şey basit ama büyük: ortada karo sunucusu yok, binlerce küçük dosya yok; indirilecek ve sürümü kontrol edilecek tek bir nesne var.
uygulama/
├─ assets/
│ └─ tr-temel.pmtiles // ülke geneli, düşük detay — pakete gömülü
└─ belgeler/ // ilk açılışta indirilen bölgeler
├─ istanbul.pmtiles
├─ toplanma-alanlari.json
└─ surum.json
Dosyanın bütünlüğünü doğrulamak, eskiyince yenisiyle değiştirmek, yer açmak gerektiğinde tek kalemde silmek — hepsi tek dosya olduğu için sade kalıyor. Aynı işleri binlerce karo dosyasıyla yapmayı denerseniz, çevrimdışı mimarinin kendisinden çok dosya sistemiyle uğraşıyorsunuz.
Asıl gerilim: boyut mu, kapsam mı
Buraya kadar her şey mantıklı görünüyor. Gerçek karar noktası şu soruda: bu verinin ne kadarı uygulamanın içinde gelecek?
| Yöntem | Kazandırdığı | Kırıldığı yer |
|---|---|---|
| Her şeyi pakete gömmek | İlk açılışta bile eksiksiz çalışır | İndirme boyutu şişer; mağaza sınırlarına ve kullanıcının sabrına takılır |
| Bölgeyi bir kez indirtmek | Boyut makul, kapsam kullanıcıya göre | İndirme anını kaçırırsanız cihaz boş kalır |
| Gezdikçe biriktirmek | Gereksiz yer harcamaz | İhtiyaç anında elde olmayan bölge, tam da gereken bölge olur |
Sağlam için tasarladığım çözüm ilk ikisinin karışımı. Uygulamanın içine ülke geneli için düşük detaylı bir temel katman gömülüyor; internet hiç görülmemiş olsa bile kullanıcı bir harita, kendi konumu ve en yakın toplanma alanlarının kaba listesini görebiliyor. Detaylı bölge paketi ise ilk kurulumda, kullanıcı hâlâ sakinken ve bağlantısı varken indiriliyor.
Bu ikinci kısım bir mühendislik sorunundan çok bir zamanlama sorunu. İndirmeyi kullanıcının ihtiyaç duyduğu anda istemek, çevrimdışı mimariyi baştan boşa çıkarıyor. Uygulamanın işi, ihtiyaç doğmadan çok önce hazır olmak.
Mağazaların indirme boyutu için üst sınırları var; ondan önce de kullanıcının sabrı devreye giriyor. Büyük paket, yarıda kalan kurulum ve mobil veriyle indirmekten vazgeçme demek. Kapsamı büyütmeden önce “bu katman gerçekten çevrimdışı gerekli mi” sorusunu her katman için ayrı sormak gerekiyor.
İnternetsiz doğrulama: Vakti Geçmeden'in QR tarafı
Aynı problemin çok daha küçük bir hâli Vakti Geçmeden'de karşıma çıktı. Bir QR kodu bir cihazda gösteriliyor, başka bir cihaz okuyup doğruluyor. Doğrulama sunucuya sorularak yapılırsa, bağlantının en kötü olduğu yerde — kalabalıkta, kapalı alanda, bodrum katta — sistem duruyor.
Çözüm, kodun kendini taşıması oldu. QR'ın içinde doğrulanacak bilginin kendisi ve imzası bulunuyor; okuyan cihazda ise imzayı kontrol edecek anahtar zaten gömülü. Böylece doğrulama tamamen cihazda yapılıyor, sonuç kuyruğa yazılıyor ve bağlantı geldiğinde sunucuya aktarılıyor.
// Sözde kod — doğrulama sırası
kod = qrOku()
if (imzaGecersiz(kod)) return REDDET
if (kod.gecerlilikSonu < suAn) return SURESI_DOLMUS
if (yerelKullanilanlar.iceriyor(kod.id)) return ZATEN_KULLANILDI
yerelKullanilanlar.ekle(kod.id)
kuyruk.ekle({ islem: "kod_kullanildi", id: kod.id })
return KABUL // son sözü, bağlantı gelince sunucu söyleyecek
Bu yaklaşımın dürüstçe söylenmesi gereken bir sınırı var: çevrimdışıyken aynı kodun iki farklı cihazda kullanılmasını engelleyemezsiniz. Yerel liste yalnızca kendi cihazınızı koruyor. Bu yüzden geçerlilik süresini kısa tutmak ve gerçek bedeli olan işlemlerde son otoriteyi sunucuya bırakmak gerekiyor. Çevrimdışı doğrulama kapıyı açık tutmak için; hesabı kapatmak için değil.
Bulutun görevi nerede başlıyor
Arka uçta yine Firebase var ama rolü değişiyor. Çevrimdışı öncelikli bir uygulamada bulut şu üç işi yapıyor:
- Paketleri güncellemek. Harita ve veri dosyalarının yeni sürümlerini yayınlamak.
- Kuyruğu toplamak. Cihazda biriken işlemleri, bağlantı gelince sırayla almak.
- Son sözü söylemek. Cihazın verdiği geçici kararları onaylamak veya geri almak.
Firestore'un kendi yerel önbelleği bu işin bir kısmını üstleniyor ve gerçekten yardımcı oluyor; ama tek başına çevrimdışı mimari değil. Önbellek daha önce okunmuş veriyi tutuyor, hiç okunmamış veriyi ilk açılışta size vermiyor. Bu yüzden “Firestore zaten çevrimdışı çalışıyor” cümlesi ilk kurulum senaryosunda doğru olmuyor.
Cihazdaki paketin eskiyip eskimediğini anlamak için tek başına duran küçük bir surum.json yetiyor. Bağlantı olduğunda önce onu kontrol etmek, gerekiyorsa yeni paketi arka planda indirmek ve indirme bittikten sonra tek seferde devreye almak, yarım kalmış güncelleme sorunlarının çoğunu ortadan kaldırıyor.
Zor kısım: test etmek
Çevrimdışı davranışı test etmenin en kolay yolu uçak modu, ama en kolay senaryo da o. Uygulamaları asıl bozan durum bağlantının olmaması değil, yarım olması. İstek gidiyor, cevap dönmüyor; indirme ortasında kesiliyor; sinyal iki saniyede bir gidip geliyor.
Kendi kontrol listem şu:
- Temiz kurulum, hiç internet görmemiş cihaz, ilk açılış uçak modunda.
- Paket indirme sırasında bağlantıyı kesmek ve uygulamayı kapatmak.
- Tamamen kapalı değil, yavaş ve kesintili bağlantı.
- Diskin neredeyse dolu olduğu cihaz.
- Uçak modunda yapılan işlemlerin, bağlantı gelince doğru sırayla gitmesi.
Bir de kullanıcı tarafı var: uygulamanın çevrimdışı olduğunu ve elindeki verinin ne zamana ait olduğunu göstermek. “Son güncelleme: 3 gün önce” yazan bir satır, sessizce eski veri göstermekten çok daha dürüst.
Özet
Çevrimdışı çalışmak, ayarlar ekranına eklenen bir kutucuk değil. Veriyi, haritayı ve hesabı cihaza taşımaya karar verdiğiniz anda uygulamanın boyutu, güncelleme akışı ve sunucunun rolü baştan değişiyor. Bu üçünü birbirinden ayrı düşünmek de mümkün olmuyor.
Sağlam'ı hâlâ tasarım aşamasında tutmamın sebebi biraz da bu: bu kararlar yanlış verilirse sonradan düzeltmek, uygulamayı baştan yazmak anlamına geliyor. Vakti Geçmeden'de QR doğrulamasını cihaza taşırken de aynı şeyi gördüm — internetin olmadığı yerde çalışan bir özellik, etrafındaki her şeyin yeniden düşünülmesini gerektiriyor.
- Çevrimdışı
- Mimari
- Harita
- Flutter
- Firebase
Demir Taşdemir
Mobil Uygulama & Web Geliştirici
2018'den beri yazılım geliştiriyorum. App Store ve Google Play'de 11 uygulama yayınladım; şu an 6 mobil uygulama, 1 e-ticaret platformu ve 1 masaüstü oyun üzerinde çalışıyorum.
Çevrimdışı çalışması gereken bir ürün mü var?
Veriyi, haritayı ve hesabı cihaza taşımanın nereye kadar mantıklı olduğunu birlikte konuşalım.