Tüm Yazılar
KategoriiOS
Okuma Süresi
13 dk
Yayın Tarihi
2026-05-13
Kelime Sayısı
711kelime

Kahveni hazırla - bu içerikli bir makale!

ESP-Point: IoT Donanım + iOS Sync için Offline-First Mimari

Özet

Kendi yan projem ESP-Point'in mimari tasarım notları: ESP32 device ↔ iOS app çift yönlü sync, BLE handshake + Firebase + Realm offline cache. 50ms handshake hedefi, 3 günlük offline dayanım, OTA firmware update. BLE state machine fragmentation asıl konu.

  • Hedefler: 50ms BLE handshake median, connection drop rate %1 altı (endüstri %4-6)
  • 3 günlük offline dayanım Realm sync queue + Firebase ile kurgulanıyor
  • OTA firmware update 244 byte chunk, %99 üstü başarı hedefi, 4-6 dakika sürer
  • CBCentralManagerOptionRestoreIdentifierKey olmadan app kill sonrası bağlantı kaybolur
ESP-Point: IoT Donanım + iOS Sync için Offline-First Mimari
ESP-Point kendi yan projem: ESP32 tabanlı küçük bir IoT cihazı ve iOS uygulaması, hâlâ geliştirme aşamasında. Aşağıdakiler ölçülmüş ürün sonuçları değil; mimari tasarım kararları ve tasarım hedefleri.

Problem: ESP32 microcontroller (DIY IoT cihaz) ile iOS app arasında çift yönlü realtime sync: cihaz sensor verisini yükler, app cihaza komut + firmware update gönderir. Offline-first zorunlu — cihaz internetsiz lokasyonlarda kullanılacak.

Tasarım hedefleri: 50ms BLE handshake median, 3 günlük offline dayanım (local cache + delayed sync), OTA firmware update için %99 üstü başarı oranı. Üçü de hedef — ölçülmüş sonuç değil.

1. Architecture

swift
1[ESP32 Device]
2 ↓ BLE (peripheral)
3[iOS App — central]
4 ↓ Wi-Fi when available
5[Firebase Realtime DB + Cloud Storage]
6 ↓ sync queue
7[Web Dashboard for fleet ops]

iOS app proxy / router rolünde: BLE \<-\> Cloud bridge.

2. BLE State Machine: Gerçek Konu

BLE'nin tek "easy demo"su olmaz. State machine fragmentation real production konu:

States:

  • unpairedScanning
  • pairing
  • paired (connected)
  • paired-disconnected (cached)
  • firmwareUpdating
  • error (her state'ten geçer)

Her transition için iOS CBCentralManager callback'lerinde 2-3 race condition çözmek gerekiyor:

  1. didDisconnect + didFailToConnect overlap — UUID-based dedup
  2. scanForPeripherals background restoration — CBCentralManagerOptionRestoreIdentifierKey set
  3. Bluetooth state .poweredOff → .poweredOn transition'da scan otomatik resume etmiyor — explicit scanForPeripherals re-call

Hedef: 50ms handshake median, connection drop rate %1 altı (endüstri standardı %4-6).

3. Offline Storage: Realm + Sync Queue

3-day offline target. Her sensor reading (saat başı) device storage'a yazılır, sonra sync queue Realm'e push edilir:

swift
1class SyncQueue: Object {
2 @Persisted(primaryKey: true) var id: ObjectId
3 @Persisted var payload: Data
4 @Persisted var createdAt: Date
5 @Persisted var retryCount: Int = 0
6 @Persisted var lastError: String?
7}

Sync worker:

swift
1final class SyncWorker {
2 func runOnce() async {
3 let realm = try await Realm()
4 let pending = realm.objects(SyncQueue.self)
5 .where { $0.retryCount < 5 }
6 .sorted(by: \.createdAt)
7 .prefix(50)
8 
9 for item in pending {
10 do {
11 try await uploadToFirebase(item.payload)
12 try realm.write { realm.delete(item) }
13 } catch {
14 try realm.write {
15 item.retryCount += 1
16 item.lastError = error.localizedDescription
17 }
18 }
19 }
20 }
21}

Network reachability NWPathMonitor ile sync worker tetiklenir. App background'da BGAppRefreshTask ile day-1 retry.

4. OTA Firmware Update

8-12MB binary blob transfer over BLE:

  • Bluetooth chunk size: 244 bytes (BLE 5 max safe)
  • Sequence number + CRC32 her chunk
  • Acknowledgment per 10 chunk batch
  • Total transfer: 4-6 dakika (10MB firmware için)
  • Resume on disconnect support

Hedef success rate: %99 üstü. Bu bütçeyi tüketen failure mode'lar:

  • connection drop son chunk öncesi (en sık vaka)
  • device flash corrupt (re-upload ile kurtarılır)
  • kritik anda power loss (manuel recovery gerekir)

OTA UI önemli — progress bar + "do not close app" warning + battery indicator. Bu üçü olmadan OTA ekranı doğrudan negatif App Store yorumu üretir.

5. Test Strategy

  • Unit tests: Sync queue state machine, BLE manager state transitions (mocked CBCentralManager)
  • Integration tests: Realm migration, sync conflict resolution
  • Device farm planı: 6 fiziksel ESP32 + 4 iPhone (iOS 16-18) — CI pipeline'da physical test
  • Snapshot tests: UI state for each BLE state

Coverage hedefi: Domain layer %90+, BLE manager %75+, UI %65 bandı.

6. iOS App Architecture

  • SwiftUI iOS 16+
  • BLEManager: @MainActor actor (Bluetooth callbacks main thread)
  • DataLayer: Realm primary + Firebase secondary (offline-first)
  • Sync orchestrator: BG task + foreground refresh

Memory bütçesi: baseline 105MB, OTA sırasında peak 220MB (firmware buffer).

7. Öğrenimler

1. BLE physical testing zorunlu. Simulator BLE'yi simüle edemez; CI'da fiziksel cihaz şart.

2. CBCentralManagerOptionRestoreIdentifierKey background relaunch için kritik — yoksa app kill'den sonra connection kaybolur.

3. State machine documentation. 6 state'li bir makinede production debug'ı için her transition log'lanmalı.

4. Realm migration schema değişikliklerinde sync queue'yu boşaltabilir — migration testi yazılmazsa bu, kullanıcı tarafında sessiz veri kaybına dönüşür.

5. OTA "abort" button'ı kullanıcıya verme — corrupt flash riski. Sadece pause/resume.

6. Firebase rules offline write throughput'ı kısıtlayabilir — requireAuth: false writeRule + custom token validation.

7. Battery profiling şart: sürekli BLE scan telefonu 8 saat bandına düşürür. Connection-on-demand pattern bu kalemde ~%35 tasarruf verir.

Sonuç: IoT + iOS sync, "happy path" demolarının gösterdiğinden kat kat zor. State machine + offline storage + OTA, baştan planlanmazsa aylarla ölçülen teknik borç demek.

Etiketler

#Case Study#IoT#BLE#Bluetooth#Offline-First#Realm#Firebase
Muhittin Çamdalı

Muhittin Çamdalı

Senior Mobile Engineer

12+ yıllık deneyime sahip Senior Mobile Engineer. iOS, Flutter ve React Native ile mobil ürünler geliştiriyorum; full-stack ve AI-native çalışıyorum.

iOS Geliştirme Haberleri

Haftalık Swift tips, SwiftUI tricks ve iOS best practices. Spam yok, sadece değerli içerik.

Onay e-postasındaki bağlantıyı açıp “Aboneliğimi onayla” düğmesine bastığında aboneliğin başlar. Bültende açılma/tıklama istatistikleri tutulur; dilediğin an tek tıkla ayrılabilirsin. Gizlilik

Paylaş

İlgili İçerik