Data persistence ensures that user information is preserved between sessions of a mobile application. Without this technology, every launch would start from scratch — settings, history and downloaded files would be lost upon closing. According to Google Developers, 2024, more than 90% of mobile apps use at least one persistence mechanism to preserve user data and interface state.
Key Takeaways
Data Persistence is the ability of an application to save data in the device’s non-volatile memory. In mobile development, persistence encompasses databases, the file system, settings and cache. Each mechanism has its own characteristics in terms of performance, security and storage capacity.
Volatile data exists only in RAM and is lost when the process terminates. This includes screen state, temporary computations and image caches. Persistent data is written to a file system or database and remains available after the app is restarted. This includes user settings, authorization tokens, operation history and downloaded content.
When selecting a storage method, developers evaluate several factors. Data type determines the storage structure: simple settings — SharedPreferences or DataStore, structured records — SQLite or Room, files — File Storage. Data volume affects performance: databases are optimized for thousands of records, while files suit large binary objects. Security requires encrypting sensitive information via EncryptedSharedPreferences or SQLCipher.
data class StorageOption(
name: String,
dataType: StorageType,
capacity: Long,
secure: Boolean
)
enum class StorageType {
KEY_VALUE,
RELATIONAL,
FILE
}
SharedPreferences is the classic way to store key-value pairs on Android. This API has been available since the early versions of the platform and supports primitive types: strings, numbers, boolean values. Data is stored in an XML file in the app’s private directory and is accessible only to its process.
Jetpack DataStore is a modern replacement for SharedPreferences built on Kotlin Coroutines and Flow. DataStore offers two variants: Preferences DataStore for simple values and Proto DataStore for typed objects. Unlike SharedPreferences, DataStore guarantees data consistency under concurrent access and supports asynchronous operations without blocking the main thread.
// SharedPreferences — the traditional approach
val prefs = context
.getSharedPreferences("settings", Context.MODE_PRIVATE)
with (prefs.edit()) {
putString("username", "john_doe")
putInt("score", 1500)
apply()
}
// DataStore — asynchronous approach
val settingsDataStore = context
.createDataStore("settings.pb")
val usernameFlow: Flow<String> = settingsDataStore
.data
.map { it[USERNAME_KEY] ?: "" }
On iOS, the equivalent of SharedPreferences is UserDefaults, a storage system for simple values in Property List format. UserDefaults uses synchronous access and is suitable for small amounts of configuration data, but is not recommended for storing sensitive information.
SQLite is an embedded relational database that runs inside the app process without a separate server. It is the most widespread DBMS in mobile development: it is used by default on both platforms. Android includes SQLite in the SDK, and iOS includes it in the libsqlite3 library. SQLite supports standard SQL, transactions, indexes and triggers.
Working with SQLite starts with defining the database schema. The developer defines tables, their fields and types, then performs insert, read, update and delete operations. SQLiteOpenHelper on Android manages database creation and migrations, while iOS uses the C interface or the FMDB wrapper.
// SQLiteOpenHelper on Android
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)
}
}
| Data Type | SharedPreferences | SQLite | File System |
|---|---|---|---|
| Type | key-value | relational DB | binary files |
| Volume | hundreds of records | thousands of records | available space |
| Performance | high | medium | depends on size |
| Typical use | settings | structured data | images, videos |
Room is a library from Jetpack that provides an ORM layer on top of SQLite. Room eliminates the repetitive work of writing SQL queries and ContentValues, replacing them with annotations and Kotlin functions. The Room compiler generates DAO (Data Access Object) implementations at build time, eliminating SQL syntax errors.
To integrate Room, you need to add the kapt dependency and annotate the entity class, DAO interface and database class. RoomDatabase serves as the entry point: you obtain a DAO through it and perform database operations. Room supports Flow for reactive queries, schema migrations and compile-time query verification.
@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 is Apple’s framework for managing an object graph and persisting it to disk. Unlike Room, Core Data works not with tables but with managed objects (NSManagedObject) that form a hierarchy of relationships. Core Data supports lazy loading, undo management and complex queries via NSFetchRequest.
The core of Core Data consists of three components: a managed object context (NSManagedObjectContext), a persistent store coordinator (NSPersistentStoreCoordinator) and a data model (NSManagedObjectModel). NSPersistentContainer unifies all components into a single entry point, simplifying setup for modern Swift applications.
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 is used for saving images, videos and documents. Android provides internal storage (context.filesDir) — private to the app, and external storage (Environment.getExternalStorageDirectory) — accessible to other apps. On iOS, files are saved in Documents and Library directories, with Library/Caches intended for cache that does not back up to iCloud. For file operations, both platforms provide File API and streaming read/write operations. Modern libraries like Coil and SDWebImage add a caching layer, combining file storage with RAM for optimal performance.
On Android, an alternative to Core Data in terms of complexity and functionality is Realm, an object database that works directly with models without an SQL layer. Realm is faster than SQLite for read operations and supports live objects that automatically update the UI when data changes.
Frequently Asked Questions
Data Persistence refers to mechanisms for saving data in the device’s non-volatile memory, ensuring its availability after the app is restarted. These include databases, file storage and settings systems.
Room is an ORM wrapper over SQLite that eliminates manual SQL query writing and ContentValues. Room verifies SQL queries at compile time, supports Kotlin Coroutines and Flow, and automatically generates data access code.
SharedPreferences is suitable for storing small amounts of simple data: app settings, flags, identifiers and user preferences. For complex or structured data, Room or DataStore is a better choice.
Core Data is Apple’s framework for managing an object graph. It provides managed objects, change tracking, lazy loading and automatic persistence to a store (SQLite, XML or binary format).
The choice depends on data complexity: for settings — DataStore or UserDefaults, for structured records — Room (Android) or Core Data (iOS), for files — File Storage. Criteria include data volume, performance requirements and encryption needs. Combining several mechanisms in one app is standard practice, allowing you to leverage the strengths of each approach.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also