MiDiccio の同期の修正と、削除の見直し
単語帳アプリ MiDiccio の v1.4.0 と v1.4.1 で、クラウド同期と削除の仕組みを見直しました。
前半は使っている方に向けた変更点のまとめ、後半は開発者向けの仕組みの解説です。必要なところだけ読んでください。
MiDiccio について
セクション「MiDiccio について」MiDiccio は、自分で言葉を登録していくオフラインの単語帳アプリです。
- サインアップ不要、広告なし
- データはまず端末に保存
- 複数の端末で使うときは、ご自身の iCloud(iOS)または Google Drive(Android)で同期(アプリ独自のサーバーはありません)
変わったこと
セクション「変わったこと」v1.4.0:同期の修正
セクション「v1.4.0:同期の修正」- 編集していない言葉の更新日時が変わらなくなりました。 これまで iOS では、同期のたびに更新日時が同期した時刻に書き換わっていました。
- バックアップから復元した言葉が、ほかの端末にも届くようになりました。 これまでは復元した端末の中にしかありませんでした。アップデート後の最初の同期で、手元の言葉をすべて一度クラウドに送り直します。
- アプリを開いたときにも同期するようになりました。 ほかの端末で追加した言葉が、開いてすぐ一覧に出ます。
v1.4.1:ゴミ箱の追加
セクション「v1.4.1:ゴミ箱の追加」- 削除した言葉を元に戻せるようになりました。 設定画面の「ゴミ箱」から、1件ずつ戻せます。
- どの端末で削除した言葉も、ほかの端末のゴミ箱から戻せます。 戻した言葉は、同期でほかの端末にも反映されます(すべての端末を v1.4.1 にしている場合)。
- ゴミ箱から完全に消す機能はまだありません。理由は後半で説明します。
開発メモ
セクション「開発メモ」ここからは仕組みの話です。
同期のルール
セクション「同期のルール」- 同じ言葉が端末とクラウドの両方にあるときは、更新日時が新しい方を採用する(Last-Write-Wins)
- 端末で保存・削除した言葉には「未送信」の印を付け、次の同期で印の付いた言葉だけを送る
個人で使うアプリなので、同じ言葉を2台で同時に編集することはまれです。競合を画面で解決してもらう仕組みは作らず、この単純なルールにしています。
不具合1:変更していない言葉を、毎回「競合」と判断していた
セクション「不具合1:変更していない言葉を、毎回「競合」と判断していた」更新日時が同じ言葉を「どちらが新しいか決められない」として、競合扱いにしていました。競合のときは端末側を残し、更新日時を今の時刻にしてクラウドに送り直します。ここに CloudKit(iCloud)の事情が重なりました。
- 端末の中では、日時をマイクロ秒まで持っている
- CloudKit に保存すると、日時はミリ秒に丸められる
- iOS では送信後、保存されたレコード(丸めた日時)で端末側を上書きする
- → 次の同期でも日時が一致して再び競合になり、更新日時が同期のたびに進む
修正
- 更新日時をミリ秒単位で比べる
- 日時が同じで中身(言葉・意味・メモ・タグなど)も同じなら、同期済みとして何もしない
- 日時が同じで中身が違うときだけ、競合として扱う
不具合2:復元した言葉に「未送信」の印が付いていなかった
セクション「不具合2:復元した言葉に「未送信」の印が付いていなかった」JSON バックアップの読み込みは、クラウドから受け取った言葉を取り込む処理を使い回していました。クラウドから来た言葉は送り直す必要がないので、印を付けない作りです。しかし、復元した言葉はまだクラウドに届いていません。
修正
- 復元した言葉に「未送信」の印を付ける
- すでに復元済みの言葉を取りこぼさないよう、アップデート後の最初の同期で、全件を一度だけ送る
- この全件送信はクラウド側の変更を取り込んだあとに行うので、クラウドの新しい内容を古い内容で上書きしない
削除は「印を付けるだけ」
セクション「削除は「印を付けるだけ」」MiDiccio の削除は、もともと言葉を消さずに「削除済み」の印を付けるだけです(ソフトデリート)。片方の端末で本当に消すと、もう片方からの同期で「クラウドにだけある言葉」として戻ってきてしまうためです。
ただし iOS だけは、ほかの端末から削除済みの言葉を受け取ったとき、端末の中で本当に消していました。Android は印を付けたまま保存しています。
v1.4.1 の変更
- iOS でも削除済みの言葉を印付きのまま保存し、すべての端末で状態をそろえる
- ゴミ箱から戻すと、印を外して更新日時を新しくする → Last-Write-Wins でほかの端末にも伝わる
削除を「編集の一種」として扱っていたので、同期に新しいルールを足さずにゴミ箱を作れました。
「完全に削除」がまだない理由
セクション「「完全に削除」がまだない理由」- 端末の中で消しても、ほかの端末やクラウドには残っているので、次の同期で戻ってくる
- 完全に消すには、「完全に削除した」という情報を全端末とクラウドに伝える仕組みが別に必要
- 古いバージョンのアプリが混ざっている場合も考えなければならない
個人で使う規模なら、削除済みの言葉が残っていても、容量や動作の速さにはほとんど影響しません。今回は「元に戻せること」を優先しました。
これからのこと
セクション「これからのこと」- 復元の取り消し: 間違ったバックアップファイルで復元したとき、直前の状態に1回分だけ戻せるようにする予定です。
- ゴミ箱の整理: 古い削除済みの言葉を一覧に出さないようにするか、全端末から完全に消す仕組みを作るかを検討しています。
ダウンロード
セクション「ダウンロード」v1.4.1 は App Store と Google Play で公開しています。