# 「iCloud が新しければ取り込む」の関数が、何も比べていなかった：応募の記録が 20 件から 1 件になりうる

> iCloud 同期の関数は、名前に反して新しいかを見ず、変更があると丸ごと置き換えていた。新しい端末の空の状態で保存すると、別の端末の 20 件が 1 件に上書きされうる。更新時刻を比べ、空では上書きしないようにした。

- URL: https://koshinstudio.com/notes/icloud-overwrite/
- 公開: 2026-10-17
- 書いた人: Koshin（https://koshinstudio.com/about/）
- サイト: Koshin Studio

  English version (https://koshinstudio.com/en/notes/icloud-overwrite/)

トレカダッシュ 開発の記録 ／ 2026年8月

  iOS アプリ「トレカダッシュ (https://koshinstudio.com/works/torecadash/)」には、利用者が自分で書きためる応募の記録があります。どの抽選に応募したか、結果はどうだったか。手で積み上げた個人の記録なので、失うと取り返せません。

  2026 年 8 月 23 日、まだ精読していなかったファイル（1,076 行）を読んで、この記録が、丸ごと消えうる経路を見つけました。きっかけは、関数の名前を信じていたことでした。

  この記事は、2026 年 8 月 23 日の作業の記録から書いています。

## 名前は「新しければ取り込む」、中身は「何も比べない」

  iCloud（Apple のクラウド）と同期する関数の名前は、loadFromICloudIfNewer、つまり「iCloud が新しければ、読み込む」です。ところが、中身は、新しいかどうかを一切見ておらず、iCloud に変更があると、その内容で、丸ごと置き換えていました。

  これで、何が起きうるでしょうか。

- 端末 A に、応募の記録が 20 件ある（iCloud にも 20 件）。

- 端末 B を、新しくインストールする。iCloud の最初の同期は、非同期なので、起動した直後は、記録が空のことがある。

- 端末 B で、1 件、記録を追加する。保存の処理が、[1 件] を iCloud へ上書きする。

- 端末 A が、変更の通知を受けて、20 件から 1 件に置き換わる（19 件が消える）。

  コードの中に、記録を書き出す、読み込む機能があったのは、「機種変更で、データが全部消えるのを防ぐため」と書かれていました。作った人も、この領域が壊れやすいことは、知っていたのです。

## 直し方：更新時刻を比べる、そして、空では上書きしない

- 更新時刻（entriesUpdatedAt）を、端末と iCloud の両方に保存し、相手が新しいときだけ取り込む。関数の名前どおりの動作にする。

- 更新時刻が、まだ無い古いデータ同士でも、空の内容で上書きすることだけは、必ず防ぐ。

- 新しい端末で、最初に復元するときも、iCloud 側の時刻を引き継ぐ。

## 分かったこと

> 関数の名前が仕様を語っていても、中身がそうとは限らない。

- 名前を信じず、中身を読む。「IfNewer」と名乗りながら、比べていませんでした。

- 取り返せないデータの書き込みは、特に疑う。利用者が手で積み上げた記録は、失うと戻りません。

- 「空」は、危険な値。空の内容で上書きしないことを、最後の砦にします。

- 最初の同期は、非同期。起動した直後が空でも、データが無いとは限りません。

- 未点検のファイルは、読む。大きくて、古いファイルほど、見落としが残ります。

  この話に出てくるもの

- トレカダッシュ (https://koshinstudio.com/works/torecadash/) — トレカの抽選・予約・先着の情報を集める iPhone アプリ

- Firebase Auth：アプリを完全に終了すると、匿名アカウントが量産された (https://koshinstudio.com/notes/apple-signin/) — 端末とアカウントの別の問題

- iOS アプリが固まって強制終了される (https://koshinstudio.com/notes/torecadash-watchdog/) — アプリ側の別の失敗

- 確認できなかったときに、通していた (https://koshinstudio.com/notes/fail-open/) — 名前と中身が違った別の例
