La persistenza dei dati garantisce che le informazioni dell’utente siano preservate tra le sessioni di un’applicazione mobile. Senza questa tecnologia, ogni avvio ricomincerebbe da zero — impostazioni, cronologia e file scaricati andrebbero persi alla chiusura. Secondo Google Developers, 2024, più del 90% delle applicazioni mobili utilizza almeno un meccanismo di persistenza per conservare i dati utente e lo stato dell’interfaccia.
Punti chiave
Data Persistence è la capacità di un’applicazione di salvare i dati nella memoria non volatile del dispositivo. Nello sviluppo mobile, la persistenza comprende database, file system, impostazioni e cache. Ogni meccanismo ha le proprie caratteristiche in termini di prestazioni, sicurezza e capacità di archiviazione.
I dati volatili esistono solo nella RAM e vengono persi alla terminazione del processo. Ciò include lo stato dello schermo, i calcoli temporanei e la cache delle immagini. I dati persistenti vengono scritti in un file system o database e rimangono disponibili dopo il riavvio dell’app. Ciò include le impostazioni utente, i token di autorizzazione, la cronologia delle operazioni e i contenuti scaricati.
Nella scelta di un metodo di archiviazione, gli sviluppatori valutano diversi fattori. Il tipo di dati determina la struttura: impostazioni semplici — SharedPreferences o DataStore, record strutturati — SQLite o Room, file — File Storage. Il volume dei dati influisce sulle prestazioni: i database sono ottimizzati per migliaia di record, mentre i file sono adatti per grandi oggetti binari. La sicurezza richiede la crittografia delle informazioni sensibili tramite EncryptedSharedPreferences o SQLCipher.
data class StorageOption(
name: String,
dataType: StorageType,
capacity: Long,
secure: Boolean
)
enum class StorageType {
KEY_VALUE,
RELATIONAL,
FILE
}
SharedPreferences è il metodo classico per memorizzare coppie chiave-valore su Android. Questa API è disponibile dalle prime versioni della piattaforma e supporta i tipi primitivi: stringhe, numeri, valori booleani. I dati sono memorizzati in un file XML nella directory privata dell’app e sono accessibili solo al suo processo.
Jetpack DataStore è un moderno sostituto di SharedPreferences basato su Kotlin Coroutines e Flow. DataStore offre due varianti: Preferences DataStore per valori semplici e Proto DataStore per oggetti tipizzati. A differenza di SharedPreferences, DataStore garantisce la coerenza dei dati in caso di accesso concorrente e supporta operazioni asincrone senza bloccare il thread principale.
// SharedPreferences — l’approccio tradizionale
val prefs = context
.getSharedPreferences("settings", Context.MODE_PRIVATE)
with (prefs.edit()) {
putString("username", "john_doe")
putInt("score", 1500)
apply()
}
// DataStore — approccio asincrono
val settingsDataStore = context
.createDataStore("settings.pb")
val usernameFlow: Flow<String> = settingsDataStore
.data
.map { it[USERNAME_KEY] ?: "" }
Su iOS, l’equivalente di SharedPreferences è UserDefaults, un sistema di memorizzazione di valori semplici in formato Property List. UserDefaults utilizza l’accesso sincrono ed è adatto a piccoli volumi di dati di configurazione, ma non è raccomandato per la memorizzazione di informazioni sensibili.
SQLite è un database relazionale integrato che funziona all’interno del processo dell’app senza un server separato. È il DBMS più diffuso nello sviluppo mobile: viene utilizzato di default su entrambe le piattaforme. Android include SQLite nell’SDK, mentre iOS lo include nella libreria libsqlite3. SQLite supporta SQL standard, transazioni, indici e trigger.
Il lavoro con SQLite inizia definendo lo schema del database. Lo sviluppatore definisce tabelle, campi e tipi, quindi esegue operazioni di inserimento, lettura, aggiornamento ed eliminazione. SQLiteOpenHelper su Android gestisce la creazione e le migrazioni del database, mentre iOS utilizza l’interfaccia C o il wrapper FMDB.
// SQLiteOpenHelper su 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)
}
}
| Tipo di dati | SharedPreferences | SQLite | File system |
|---|---|---|---|
| Tipo | chiave-valore | DB relazionale | file binari |
| Volume | centinaia di record | migliaia di record | spazio disponibile |
| Prestazioni | elevate | medie | dipende dalle dimensioni |
| Uso tipico | impostazioni | dati strutturati | immagini, video |
Room è una libreria di Jetpack che fornisce un livello ORM su SQLite. Room elimina il lavoro ripetitivo di scrivere query SQL e ContentValues, sostituendoli con annotazioni e funzioni Kotlin. Il compilatore di Room genera implementazioni DAO (Data Access Object) al momento della compilazione, eliminando gli errori di sintassi SQL.
Per integrare Room, è necessario aggiungere la dipendenza kapt e annotare la classe entità, l’interfaccia DAO e la classe del database. RoomDatabase funge da punto di ingresso: attraverso di essa si ottiene un DAO e si eseguono operazioni sul database. Room supporta Flow per query reattive, migrazioni dello schema e verifica delle query al momento della compilazione.
@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 è il framework di Apple per gestire un grafo di oggetti e persistirlo su disco. A differenza di Room, Core Data non lavora con tabelle ma con oggetti gestiti (NSManagedObject) che formano una gerarchia di relazioni. Core Data supporta il caricamento lazy, l’annullamento delle modifiche e query complesse tramite NSFetchRequest.
Il nucleo di Core Data è composto da tre componenti: un contesto di oggetti gestiti (NSManagedObjectContext), un coordinatore di archiviazione persistente (NSPersistentStoreCoordinator) e un modello di dati (NSManagedObjectModel). NSPersistentContainer unifica tutti i componenti in un unico punto di ingresso, semplificando la configurazione per le moderne applicazioni 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)")
}
}
}
L’archiviazione di file (File Storage) viene utilizzata per salvare immagini, video e documenti. Android fornisce archiviazione interna (context.filesDir) — privata all’app, ed esterna (Environment.getExternalStorageDirectory) — accessibile ad altre app. Su iOS, i file vengono salvati nelle directory Documents e Library, dove Library/Caches è destinata alla cache che non viene sottoposta a backup su iCloud. Per le operazioni sui file, entrambe le piattaforme forniscono File API e operazioni di lettura/scrittura in streaming. Librerie moderne come Coil e SDWebImage aggiungono un livello di caching, combinando l’archiviazione su file con la RAM per prestazioni ottimali.
Su Android, un’alternativa a Core Data per complessità e funzionalità è Realm, un database orientato agli oggetti che lavora direttamente con i modelli senza uno strato SQL. Realm è più veloce di SQLite nelle operazioni di lettura e supporta oggetti live che aggiornano automaticamente l’interfaccia quando i dati cambiano.
Domande frequenti
Data Persistence si riferisce ai meccanismi per salvare i dati nella memoria non volatile del dispositivo, garantendone la disponibilità dopo il riavvio dell’app. Questi includono database, archiviazione di file e sistemi di impostazioni.
Room è un livello ORM su SQLite che elimina la scrittura manuale di query SQL e ContentValues. Room verifica le query SQL al momento della compilazione, supporta Kotlin Coroutines e Flow, e genera automaticamente il codice di accesso ai dati.
SharedPreferences è adatto per memorizzare piccole quantità di dati semplici: impostazioni dell’app, flag, identificatori e preferenze utente. Per dati complessi o strutturati, è meglio usare Room o DataStore.
Core Data è il framework di Apple per la gestione di un grafo di oggetti. Fornisce oggetti gestiti, tracciamento delle modifiche, caricamento lazy e persistenza automatica in un archivio (SQLite, XML o formato binario).
La scelta dipende dalla complessità dei dati: per le impostazioni — DataStore o UserDefaults, per record strutturati — Room (Android) o Core Data (iOS), per i file — File Storage. I criteri includono il volume di dati, i requisiti di prestazioni e la necessità di crittografia. Combinare più meccanismi in un’app è una pratica standard che consente di sfruttare al meglio i punti di forza di ciascun approccio.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche