Vai al contenuto

PERSISTENZA DEI DATI

Cosa imparerò:

  • Protocollo Codable
  • Sandboxing
  • Come salvare i dati in locale

Il protocollo Codable ci permette di fare in modo che un’istanza possa essere codificata/decodificata o serializzata in dati che possono essere scritti in un file su disco.

Utilizzando un formato speciale chiamato Property List, plist, simile ad un dizionario possiamo salvare dei dati sul dispositivo (in locale).

Una volta che la classe o la struct è conforme al protocollo possiamo utilizzare un oggetto Encoder per codificare l’oggetto in un valore di tipo Data che può essere salvato. Attraverso l’oggetto Decoder possiamo decodificare i dati salvati nel modello corrispondente di quei dati.

Data è una struttura di Swift che rappresenta dei dati salvati in byte. Questo tipo fornisce dei metodi per scrivere e leggere a/da un file.

struct Note: Codable {
    let title: String
    let text: String

    init(title: String, text: String) {
        self.title = title
        self.text = text
    }
}
let note = Note(title: "Titolo nota", text: "Testo della nota")
do {
    let encoder = PropertyListEncoder()
    let data = try encoder.encode(note)
    let decoder = PropertyListDecoder()
    let decodedNote = try decoder.decode(Note.self, from: data)
    print(decodedNote.title)
    print(decodedNote.text)
} catch {
    print("Errore: \(error)")
}

Output

Titolo nota
Testo della nota

Il sandboxing in iOS limita l'accesso di un'applicazione solo alle sue risorse e dati, impedendo l'accesso diretto ai dati delle altre app o del sistema operativo. Questo garantisce la sicurezza e l'isolamento delle app, proteggendo i dati dell'utente e mantenendo l'integrità del sistema operativo.

Per poter accedere ai file il framework Foundation definisce una classe FileManager che serve per interagire con i file su disco. Ha una funzione che dà accesso alla Documents directory e permette di leggere e scrivere file in quella directory.

Creiamo il path dove salvare il file:

let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!

Creiamo il file .plist nel path:

let archiveUrl = documentsDirectory.appendingPathComponent("notes_test").appendingPathExtension("plist")

Codifichiamo i dati e li salviamo nel file:

let encoder = PropertyListEncoder()
let encodedNote = try? encoder.encode(note)
try? encodedNote?.write(to: archiveUrl, options: .noFileProtection)

Per leggere i dati utilizziamo il PropertyListDecoder:

if let data = try? Data(contentsOf: archiveUrl){
    let decoder = PropertyListDecoder()
    if let decodedNote = try? decoder.decode(Note.self, from: data){
        print("Nota decodificata: \(decodedNote)")
    }
}

Se invece di un singolo oggetto volessimo salvare una collezione l’unica cosa da modificare sarebbe nella conversione dei dati:

if let data = try? Data(contentsOf: archiveUrl){
    let decoder = PropertyListDecoder() 
    if let decodedNotes = try? decoder.decode(Array.self, from: data) //[Note].self
        print("Numero note: \(decodedNotes.count)")
    }
}

Attenzione: il codable funziona sui tipi che conformano a Encodable/Decodable, per utilizzarlo con UIImage o simili bisogna convertire gli oggetti in tipo Data prima di salvarli.