Einleitung
Es war nicht schwer, aber ich würde nicht sagen, dass es Plug-and-Play ist. Ich habe ein paar Wochen gebraucht, um das Fundament korrekt zum Laufen zu bringen, wobei die größte Schwierigkeit der Mangel an Beispielen und Tutorials war. Im Vergleich zu Powersync + Supabase oder einfach Firebase erfordert dies definitiv etwas Einrichtung.
Implementierung
Es gab einen offiziellen Blogbeitrag mit einem Git-Beispiel dazu, wie man Appwrite + RxDB umsetzt. Als ich es jedoch ausprobierte, war es ziemlich fehlerhaft, und es macht keine Echtzeit-Synchronisierung. Also habe ich am Ende mein eigenes von Grund auf gebaut, indem ich sowohl die Appwrite- als auch die RxDB-Dokumentation gelesen habe.
Große Hürden
State-Management mit RxDB
Ich verwende LegendState, und es gibt Gelegenheiten, bei denen ich versuche, die Instanz meiner rxdb-Collection mit .set() zu setzen, weil das eine Schleife in meiner View verursacht. Also muss ich sicherstellen, dass ich .get() für die rxdb-Collection verwende und LegendState nutze, um Updates zu lesen und zu überwachen. Andererseits habe ich in meinem viewModel alle Funktionen, die die rxdb direkt manipulieren. Die Änderung des Appwrite-Replikats erfolgt in den viewModel-Funktionen.
Echtzeit-Synchronisierung aktualisiert die View in einer Schleife
Zusammenhängend mit dem letzten Punkt. Ich bin mit LegendState nicht vertraut, aber ich werde jetzt besser. Ich habe Debounce hinzugefügt, um das Problem zu lösen.
Synchronisierungsverzögerung bringt den lokalen State durcheinander
Da die RxDB-Appwrite-Replikation nur einen Schreibvorgang gleichzeitig ausführen kann, gibt es ein Race-Problem beim lokalen State, wann immer ich mehrere Aktionen in kurzer Zeit ausführe. Das liegt daran, dass ich die Collection in Echtzeit abonniert habe, die lokal geänderten und empfangenen Daten aber nicht synchron sind. Deshalb habe ich ein updatedAt erstellt, um die Konflikte zu behandeln.
Gelöschte Felder
Man kann ein deleted-Feld für die Replikation festlegen. Es wird empfohlen, das Löschen mit .remove() durchzuführen. Diese Löschmethode führt jedoch dazu, dass es kein “Löschen rückgängig machen” gibt. Das liegt daran, dass RxDB alles, was per .remove()-Methode soft-gelöscht wurde, verbirgt, sodass der Client nicht allein durch Prüfen von !deleted auf das Element zugreifen kann. Es gibt eine get/findone-Methode, um eine gelöschte Datei abzurufen, was ziemlich umständlich ist, aber ich verstehe, dass rxdb seinen eigenen Soft-Delete-Mechanismus braucht. Deshalb habe ich ein zusätzliches deleted-Feld für mich selbst hinzugefügt. Insgesamt gibt es also 3 deleted-Felder – rxDeleted, isDeleted, deleted (Appwrite-Standard).
Wenn ein Benutzer nun ein Element löscht, schalte ich nur isDeleted um, damit es in den Papierkorb zum Wiederherstellen wandern kann. Auf der Client-Seite richte ich eine 30-Tage-Prüfung von isDeleted ein, um das rxdb-Softdelete auszuführen, das rxDeleted umschaltet. Dann führe ich auf der Server-Seite eine weitere Prüfung durch, sagen wir 30 Tage von rxDeleted, und lösche sie wirklich.
Soft-Delete über rxDeleted ist notwendig, damit RxDB das deleted-Feld zwischen Geräten synchronisieren kann. Aber ich möchte auch, dass der Benutzer das Element leicht wiederherstellen kann, also habe ich isDeleted obendrauf hinzugefügt.
Fazit
Es war nicht ganz einfach für mich, ich habe Wochen gebraucht, um all das zu lösen, da ich mit SwiftUI angefangen habe, wo Apples CoreData alle Synchronisierungsvorgänge für mich erledigt hat. Dies ist das erste Mal, dass ich tatsächlich mehr tun muss, nur für die Synchronisierung. Aber ich bin froh, dass ich es gemacht habe.
Jetzt kann ich mich dem nächsten großen Ding widmen!