多言語対応(ローカライズ)を終えてアプリの見た目が一段落したところで、次に手を入れたのがデータ保存まわりでした。「とりあえず保存できればいい」と安易に選んだ結果、後から書き直すことになった話を記録しておきます。
1. 何でもUserDefaultsに突っ込んでいた
アプリを作り始めた頃、保存したいデータが出てくるたびに、とりあえずUserDefaults.standard.set()で保存していました。オン/オフの設定値も、記録したメモの配列も、全部同じ場所です。
UserDefaults.standard.set(memoList, forKey: "memoList")
最初はこれで動いていたのですが、メモの件数が増えてくると、アプリ起動時にもたつきを感じるようになりました。調べてみると、UserDefaultsは内部的にはplistファイル1つにすべてを読み書きしており、大きな配列やオブジェクトの保存には向いていないと知りました。「設定値のような小さなキー・バリューを保存する場所」であって、「データベースの代わり」ではなかったのです。
2. SwiftDataへの移行で気づいた設計の違い
そこでメモのようなデータはSwiftDataへ移行することにしました。@Modelでモデルを定義し直す作業です。
@Model
class Memo {
var title: String
var createdAt: Date
init(title: String, createdAt: Date = .now) {
self.title = title
self.createdAt = createdAt
}
}
移行してみて分かったのは、UserDefaultsが「値をまるごと保存・読み込みする」仕組みなのに対し、SwiftDataは「1件ずつ追加・更新・削除できるデータベース」だということです。件数が増えるほど、SwiftDataの方が扱いやすくなる一方、単純なフラグ1つを保存するだけなのにモデルを定義してコンテナを用意するのは、明らかにやりすぎでした。
3. どちらを使うか、結局の判断基準
試行錯誤の末、今は次のように使い分けています。
- UserDefaults:オン/オフの設定、選択中のテーマ、最後に開いた画面など、単一の値
- SwiftData:ユーザーが作成する複数件のデータ(メモ、記録、履歴など)、検索や並び替えが必要なもの
判断に迷ったときの自分なりの基準は「配列やリストで管理したくなるかどうか」です。件数が増減し、1件ずつ操作したくなるものはSwiftData、そうでない単発の値はUserDefaults、という線引きにしてから、どちらを使うか毎回悩むことがなくなりました。
4. 移行時にハマったマイグレーションの罠
UserDefaultsに保存していたメモの配列を、SwiftDataに移行する処理を書いたときのことです。アプリ起動時に「UserDefaultsにデータが残っていればSwiftDataに移してから削除する」というワンタイムの移行処理を入れたのですが、これをうっかり毎回の起動時に実行する場所に書いてしまい、アプリを起動するたびに同じメモが重複して登録されるバグを作ってしまいました。
// 移行済みかどうかのフラグをUserDefaultsに立てるのを忘れていた
if let oldData = UserDefaults.standard.array(forKey: "memoList") {
// ここで毎回SwiftDataに追加してしまっていた
}
移行処理には「移行済みフラグ」をUserDefaultsに立てて、2回目以降は処理をスキップする一文を追加して解決しました。データ移行は一度きりの処理だからこそ、うっかり毎回実行される場所に置いてしまわないよう注意が必要だと学びました。
まとめ
- UserDefaultsは設定値などの単一の値向き、SwiftDataは複数件のデータをまとめて扱うのに向いている
- 迷ったときは「配列・リストで管理したくなるかどうか」で判断すると選びやすい
- UserDefaultsに何でも保存すると、データ量が増えたときにパフォーマンスの問題が出やすい
- データ移行処理は「一度きり」であることを保証する仕組み(移行済みフラグなど)を忘れずに入れる
保存先を最初にきちんと設計しておけば避けられた手戻りだったので、次にアプリを作るときは、データの性質を見てから保存方法を選ぶようにしたいと思います。
次は、個人開発を継続するために工夫していることをまとめます。
本記事は生成AI(Claude)を活用して執筆・編集しています。内容は実体験に基づき、執筆者本人が確認・監修しています。


コメント