site-icon Hong Cheung
Jun 8, 2025

終於讓 Appwrite 和 RxDB 的離線同步功能正常運作了

前言

這並不難,但我不會說它是隨插即用。我花了幾週時間才讓基礎架構正確運作,主要困難在於缺乏範例和教學。相較於 Powersync + Supabase 或直接使用 Firebase,這確實需要一些設定。

實作

有一篇官方部落格文章附帶 git 範例,說明如何實現 Appwrite + RxDB。然而,當我嘗試時,它有點問題,而且它不做即時同步。所以,我最後透過閱讀 Appwrite 和 RxDB 的文件,從頭開始建立自己的版本。

主要障礙

使用 RxDB 處理狀態管理

我使用 LegendState,有時候我會嘗試 .set() 我的 rxdb collection 實例,因為它會在我的檢視中造成迴圈。所以,我必須確保對 rxdb collection 使用 .get(),並使用 LegendState 來讀取和監控更新。另一方面,在我的 viewModel 中,我有所有直接操作 rxdb 的函式。Appwrite replica 的修改發生在 viewModel 的函式中。

即時同步更新檢視造成迴圈

與上一點相關。我對 LegendState 不熟悉,但現在越來越上手了。我加入了 debounce 來解決這個問題。

同步延遲打亂本地狀態

因為 RxDB Appwrite 複寫一次只能執行一次寫入,每當我在短時間內執行多個動作時,本地狀態就會有競爭問題。這是因為我即時訂閱了 collection,但本地變更和接收到的資料並不同步。因此,我建立了一個 updatedAt 來處理衝突。

已刪除的欄位

你可以為複寫設定 deleted 欄位。文件指示使用 .remove() 來執行刪除。然而,這個刪除方法會導致無法「復原刪除」。這是因為 RxDB 會透過它的 .remove() 方法隱藏任何被軟刪除的項目,客戶端無法僅透過檢查 !deleted 來存取該項目。有一個 get/findone 方法可以只擷取已刪除的檔案,有點麻煩,但我理解 rxdb 需要自己的軟刪除機制。因此,我為自己額外加入了一個 deleted 欄位。所以,總共有 3 個 deleted 欄位——rxDeleted、isDeleted、deleted(Appwrite 預設)。

現在,當使用者刪除一個項目時,我只切換 isDeleted,這樣它就可以進入垃圾桶以供復原。在客戶端,設定一個 30 天的 isDeleted 檢查來執行 rxdb 軟刪除,切換 rxDeleted。然後,在伺服器端,執行另一個檢查,比如 rxDeleted 30 天後,真正刪除它們。

透過 rxDeleted 進行軟刪除是必要的,這樣 RxDB 才能同步裝置的 deleted 欄位。但是,我也希望使用者能輕鬆復原項目,所以我在其上加入了 isDeleted。

結論

這對我來說並不太容易,我花了幾週才解決所有這些問題,因為我一開始是用 SwiftUI,Apple CoreData 為我處理了所有同步程序。這是我第一次真正需要為同步做更多事情。但是,我很高興我做到了。


現在,我可以著手進行下一個大計畫了!

上一條動態
Copyright 2026
網站地圖
隱私政策
服務條款

主題由 Astro-Yi