データパーシステンスは、モバイルアプリのセッション間でユーザー情報を保存します。この技術がなければ、起動ごとに一から始まり、設定、履歴、ダウンロードファイルは閉じるときに失われます。Google Developers, 2024によると、90% 以上のモバイルアプリが、ユーザーデータとインターフェース状態を保存するために、少なくとも一つのパーシステンス機構を使用しています。
まとめ
Data Persistenceとは、アプリがデバイスの非ボラタイルメモリにデータを保存できる能力です。モバイル開発において、パーシステンスには、データベース、ファイルシステム、設定、キャッシュが含まれます。それぞれの機構には、パフォーマンス、セキュリティ、容量にそれぞれ特徴があります。
一時データはRAMにのみ存在し、プロセス終了時に失われます。これには、画面状態、一時計算、画像キャッシュが含まれます。持続データは、ファイルシステムやデータベースに書き込まれ、アプリ再開後も利用可能です。これには、ユーザー設定、認証トークン、操作履歴、ダウンロードコンテンツが含まれます。
ストレージ方法を選択する際、開発者はいくつかの要因を評価します。データの種類が保存構造を決定します:単純な設定はSharedPreferencesやDataStore、構造化された記録はSQLiteやRoom、ファイルはFile Storageです。データ量はパフォーマンスに影響します:データベースは数千の記録にオプティマイズされ、ファイルは大きなバイナリオブジェクトに適しています。セキュリティのためには、EncryptedSharedPreferencesやSQLCipherによる情報暗号化が必要です。
data class StorageOption(
name: String,
dataType: StorageType,
capacity: Long,
secure: Boolean
)
enum class StorageType {
KEY_VALUE,
RELATIONAL,
FILE
}
SharedPreferencesは、Androidでキーバリュー組を保存する古典的な方法です。このAPIはプラットフォームの初期バージョンから利用可能で、基本型(文字列、数値、ブール値)をサポートしています。データはアプリのプライベートディレクトリのXMLファイルに保存され、そのプロセスからのみアクセス可能です。
Jetpack DataStoreは、Kotlin CoroutinesとFlowに基づいたSharedPreferencesの現代的な置き換えです。DataStoreには二つのバリエーションがあります:単純な値にPreferences DataStore、型付きオブジェクトにProto DataStoreです。SharedPreferencesとは異なり、DataStoreは並行アクセス下でのデータ一貫性を保証し、メインスレッドをブロックせずに非同期操作をサポートします。
// SharedPreferences — 伝統的な方法
val prefs = context
.getSharedPreferences("settings", Context.MODE_PRIVATE)
with (prefs.edit()) {
putString("username", "john_doe")
putInt("score", 1500)
apply()
}
// DataStore — 非同期アプローチ
val settingsDataStore = context
.createDataStore("settings.pb")
val usernameFlow: Flow<String> = settingsDataStore
.data
.map { it[USERNAME_KEY] ?: "" }
iOSでは、SharedPreferencesに当たるのがUserDefaultsです:Property List形式の単純な値を保存するシステムです。UserDefaultsは同期アクセスを使用し、少量の設定データに適していますが、機密情報の保存には推奨されません。
SQLiteは、独立したサーバーなしにアプリプロセス内で動作する組込み関係データベースです。モバイル開発で最も広く使われているDBMSで、両プラットフォームでデフォルトで使用されています。AndroidはSDKにSQLiteを含み、iOSはlibsqlite3ライブラリに含んでいます。SQLiteは標準SQL、トランザクション、インデックス、トリガーをサポートしています。
SQLiteの使用は、データベーススキーマの定義から始まります。開発者はテーブル、フィールド、型を定義し、挿入、読み出し、更新、削除操作を実行します。AndroidのSQLiteOpenHelperがデータベースの作成とマイグレーションを管理する一方、iOSはCインタフェースやFMDBラッパーを使用します。
// AndroidでのSQLiteOpenHelper
class DBHelper(context: Context) :
SQLiteOpenHelper(context, "app.db", null, 1) {
override fun onCreate(db: SQLiteDatabase) {
db.execSQL("""
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
email TEXT UNIQUE
)
""")
}
fun insertUser(name: String, email: String) {
val db = writableDatabase
val values = ContentValues().apply {
put("name", name)
put("email", email)
}
db.insert("users", null, values)
}
}
| データの種類 | SharedPreferences | SQLite | ファイルシステム |
|---|---|---|---|
| タイプ | キーバリュー | 関係DB | バイナリファイル |
| 容量 | 数百件 | 数千件 | 空き容量 |
| パフォーマンス | 高い | 中程度 | サイズによる |
| 典型的な用途 | 設定 | 構造化データ | 画像、動画 |
Roomは、Jetpackが提供するSQLite上のORMレイヤーを提供するライブラリです。Roomは、SQLクエリやContentValuesのタイピング作業を廃止し、アノテーションやKotlin関数で置き換えます。Roomコンパイラは、ビルド時にDAOの実装を生成し、SQL構文エラーを解消します。
Roomを組み込むには、kapt依存関係を追加し、エンティティクラス、DAOインタフェース、データベースクラスにアノテーションを付与する必要があります。RoomDatabaseがエントリポイントとなり、DAOを取得してデータベース操作を実行します。Roomは、反応的クエリのFlow、スキーママイグレーション、コンパイル時クエリ検証をサポートします。
@Entity(tableName = "users")
data class User(
@PrimaryKey val id: Int,
@ColumnInfo(name = "full_name") val name: String,
@ColumnInfo val email: String
)
@Dao
interface UserDao {
@Query("SELECT * FROM users")
fun getAll(): Flow<List<User>>
@Insert
suspend fun insert(user: User)
@Delete
suspend fun delete(user: User)
}
@Database(
entities = [User::class],
version = 1
)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}
Core Dataは、オブジェクトグラフを管理し、ディスクに保存するAppleのフレームワークです。Roomと異なり、Core Dataはテーブルではなく、関係の階層を形成する管理オブジェクト(NSManagedObject)を扱います。Core Dataは、ゆっくり読み込み、変更の元に戻す、NSFetchRequestによる複雑なクエリをサポートしています。
Core Dataの中核は三つのコンポーネントから成ります:管理オブジェクトコンテキスト(NSManagedObjectContext)、パーシステントストアコーディネータ(NSPersistentStoreCoordinator)、データモデル(NSManagedObjectModel)です。NSPersistentContainerはこれらをひとつのエントリポイントに統合し、現代的なSwiftアプリの設定を簡潔にします。
import CoreData
class PersistenceController {
static let shared = PersistenceController()
let container: NSPersistentContainer
init() {
container = NSPersistentContainer(name: "AppModel")
container.loadPersistentStores { _, error in
if let error = error {
fatalError("Failed: \(error)")
}
}
}
func saveUser(name: String, email: String) {
let context = container.viewContext
let user = User(context: context)
user.name = name
user.email = email
do {
try context.save()
} catch let error {
print("Save error: \(error)")
}
}
}
File Storageは、画像、動画、ドキュメントの保存に使用されます。Androidは内部ストレージ(context.filesDir)—アプリ固有のプライベート空間、および外部ストレージ(Environment.getExternalStorageDirectory)—他のアプリからアクセス可能な空間を提供します。iOSでは、ファイルはDocumentsとLibraryディレクトリに保存され、Library/CachesはiCloudバックアップの対象とならないキャッシュ用です。ファイル操作には、両プラットフォームがFile APIとストリーミング読み書き操作を提供しています。CoilやSDWebImageのような現代的なライブラリは、最適なパフォーマンスのためにファイルストレージとRAMを組み合わせたキャッシングレイヤーを追加します。
AndroidでCore Dataの複雑さと機能に代わるのがRealmです:SQLレイヤーなしに直接モデルを扱うオブジェクトデータベースです。Realmは読み出し操作でSQLiteより高速で、データ変更時にUIを自動更新するライブオブジェクトをサポートしています。
よくある質問
Data Persistenceとは、デバイスの非ボラタイルメモリにデータを保存する機構で、アプリ再開後も利用可能です。これには、データベース、ファイルストレージ、設定システムが含まれます。
Roomは、SQLクエリの手動記述やContentValuesを廃止するSQLite上のORMラッパーです。Roomはコンパイル時にSQLクエリを検証し、Kotlin CoroutinesやFlowをサポートし、データアクセスコードを自動生成します。
SharedPreferencesは、少量の単純なデータ(アプリ設定、フラグ、ID、ユーザー設定)の保存に適しています。複雑なデータには、RoomやDataStoreを使用するほうが良いでしょう。
Core Dataは、オブジェクトグラフを管理するAppleのフレームワークです。管理オブジェクト、変更トラッキング、ゆっくり読み込み、ストア(SQLite、XML、バイナリ)への自動保存を提供します。
選択はデータの複雑さによります:設定にはDataStoreやUserDefaults、構造化された記録にはRoom(Android)やCore Data(iOS)、ファイルにはFile Storage。基準は、データ量、パフォーマンス要件、暗号化の必要性です。複数の機構を組み合わせるのが標準的な方法で、それぞれのアプローチの強みを活かすことができます。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。