Mantenere dati e preferenze di un’app iOS quando l’utente effettua un aggiornamento da App Store

Ho di recente iniziato a sviluppare un’app di ricette per iOS, e come ogni buona app di questo tipo, vi è la possibilità di inserire una ricetta tra i preferiti.

Anche se la questione può sembrare semplice, in realtà la cosa è più complicata di quanto si pensi.

Analizzando il problema in profondità, dal punto di vista di uno sviluppatore, vi è il problema dell’aggiornamento dell’app.

Se io, utente, aggiorno la mia applicazione, e tra i preferiti avevo salvato 10 ricette, dopo l’aggiornamento, che fine fanno i preferiti? E’ lecito pensare, dal punto di vista di uno sviluppatore, che un aggiornamento comporti l’eliminazione della vecchia versione dell’app, dopodiché l’installazione della nuova release.

Questo problema, che può sembrare banale, in realtà non è così semplice. Ho dovuto cercare e alla fine capire da solo come risolvere il problema. Nemmeno Apple, nella sua documentazione, accenna al problema.

Andrò quindi ad analizzare la mia situazione, che può essere utile praticamente per tutte le app che necessitano della persistenza dei dati,  anche dopo un aggiornamento dell’app stessa tramite app store.

Analisi

L’applicazione sul quale ho affrontato il problema è così strutturata: ho un file .plist, contenente le mie ricette. Il plist è composto da un array, l’array al proprio interno contiene N dictionary, ognuno dei quali identifica una ricetta. Ogni dictionary (quindi ogni singola ricetta) ha al proprio interno i campi che costituiscono la ricetta (titolo, ingredienti, preparazione, categoria, etc…).

Questo .plist, verrà aggiornato di volta in volta dallo sviluppatore (da me), quando viene rilasciato un aggiornamento dell’applicazione su app store. E’ palese il fatto che NON possiamo inserire in questo caso un nuovo campo booleano “preferito” all’interno di ogni ricetta, poiché il file verrà sovrascritto durante l’aggiornamento, e quindi se l’utente aveva inserito delle ricette tra i preferiti, dopo l’aggiornamento tutti i campi “preferito” tornerebbero a FALSE, in sostanza: abbiamo perso tutti i preferiti.

plist app ricette

File System

Per comprendere a fondo il problema, è essenziale capire come funziona il file system su iOS.

La documentazione ufficiale di Apple, spiega in modo chiaro e semplice il funzionamento del file system.

In breve, ogni applicazione ha una Sandbox, ovvero un ambiente dove effettuare operazioni di IN/OUT, dove vi è la possibilità di memorizzare qualsiasi tipo di file. Lo sviluppatore, NON può accedere al di fuori della Sandbox, e ogni app ha una propria Sandbox.

sandbox di ios

All’interno della Sandbox, vi sono cinque directory, utili allo sviluppatore per lo storage dei dati. Di seguito vi è una traduzione italiana, presa da devapp, di ciò che trovate sulla guida ufficiale Apple.

ios File System directory

Siccome si presume che le nostre preferenze, che vogliamo ritrovare dopo ogni aggiornamento dell’app, siano anche “backuppate” da iTunes, dobbiamo utilizzare in questo specifico caso la directory “Documents”.

Aggiornamento da App Store

Come funziona l’aggiornamento di un’app secondo Apple? iTunes scarica l’aggiornamento in un una nuova directory, dopodiché sposta i dati contenuti nelle directory sopra elencate, dalla vecchia versione dell’app alla nuova. Se nella nuova versione dell’app, all’interno della directory Documents, viene scritta una nuova versione del nostro file (ad esempio del .plist contenente le ricette), automaticamente viene eliminata la vecchia versione. Ecco spiegato il perché non possiamo inserire i preferiti all’interno di ricette.plist. Non possiamo accedere contemporaneamente alla vecchia e alla nuova versione del file per fare un merge, dobbiamo adottare un’altra soluzione.

La soluzione

La soluzione è un secondo file (in questo caso ho utilizzato un altro .plist chiamandolo “Preferiti.plist”).

file preferiti plist struttura

In questo file, strutturato come un Dictionary contenente un solo Array di nome HandSet (che al suo interno conterrà solo tipi String), memorizzerò i titoli di ogni singola ricetta (o gli ID). Se la ricetta è all’interno di questo file, significa che è tra i preferiti, e se aggiornerò il file Ricette.plist, non dovrò occuparmi di quali ricette sono tra i preferiti o meno, perché è in un file a parte. Questo significa che se una ricetta era tra i preferiti, rimane dov’è, e all’utente viene data la possibilità di inserire nuove ricette tra i preferiti.


//Dichiaro la path del file Preferiti.plist presente nel bundle, che sarà

//successivamente scritta in Documents (all'interno della sandbox)

NSString *finalPath = [[NSBundle mainBundle] pathForResource:@"Preferiti" ofType:@"plist"];

/* PERMETTE DI SCRIVERE IL FILE "Preferiti.plist" NELLA CORRETTA CARTELLA "Documents" DELLA SANDBOX */

NSFileManager *fileManager = [NSFileManager defaultManager];

NSError *error;

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

NSString *documentsDirectory = [paths objectAtIndex:0];

NSString *DocPath = [documentsDirectory stringByAppendingPathComponent:@"Preferiti.plist"];

if ([fileManager fileExistsAtPath:DocPath] == NO) { //se Preferiti.plist non esiste (è la prima volta che l'app viene aperta) lo scrive.

[fileManager copyItemAtPath:finalPath toPath:DocPath error:&error];

NSLog(@"SCRITTO FILE INIZIALE Preferiti.plist");

}

//se invece è un aggiornamento, non deve fare nulla, perché significa che già esiste un file Preferiti.plist nella directory Documents

Quando effettuiamo un’operazione sul file .plist, dobbiamo stare ben attenti a prelevare ciò che era già presente al suo interno, dopodiché effettuare un merge tra i vecchi dati e quelli che stiamo andando ad inserire (in questo caso, un nuovo preferito). Sui file .plist non possiamo semplicemente aggiungere un oggetto, in quanto ogni volta che richiameremo

[dati writeToFile:path atomically:YES];

verrà creato e scritto un nuovo file (e se già presente, il vecchio sarà sovrascritto).

//AGGIUNTA DI UN NUOVO VALORE IN PREFERITI.PLIST
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

NSString *documentsDirectory = [paths objectAtIndex:0];

NSString *DocPath = [documentsDirectory stringByAppendingPathComponent:nomePlist];

NSMutableDictionary *addData = [NSMutableDictionary dictionaryWithContentsOfFile:DocPath];

NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:DocPath];

NSArray *oldHandsets = [dict valueForKey:@"HandSet"]; //vecchi dati presente in preferiti.plist

NSArray *newHandsets = [[NSArray alloc]initWithObjects:@"nuovo preferito string", nil]; //nuovo oggetto-array di oggetti da inserire tra i preferiti

NSMutableSet *mergeHandsets = [NSMutableSet setWithArray:oldHandsets];

[mergeHandsets addObjectsFromArray:newHandsets]; //merge dei dati (vecchi e nuovi)

//aggiungo i nuovi oggetti al plist, nell'Array di nome HandSet

[addData setObject:[mergeHandsets allObjects] forKey:@"HandSet"];

//scrivo il nuovo file con i cambiamenti apportati

[addData writeToFile:DocPath atomically:YES];

E per eliminare un dato?


 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *DocPath = [documentsDirectory stringByAppendingPathComponent:nomePlist];

    NSMutableDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:DocPath];

    NSMutableArray *dataFromPlist = [dict valueForKey:@"HandSet"];

    [dataFromPlist removeObject:@"string da rimuovere"];

    [dict setObject:dataFromPlist forKey:@"HandSet"];

    [dict writeToFile:DocPath atomically:YES];

Ovviamente, prima di cancellare un dato dovete assicurarvi che sia presente, quindi tocca a voi gestire tutte le varie eccezioni che potrebbero essere generate.

Utilizzando questo sistema, possiamo memorizzare delle preferenze o un qualsiasi tipo di dato (e non per forza un file .plist), senza perdere informazioni durante un aggiornamento eseguito tramite app store.

Se questa guida ti è stata utile, fammelo sapere nei commenti! 😉

Cheers!

4 thoughts on “Mantenere dati e preferenze di un’app iOS quando l’utente effettua un aggiornamento da App Store

  1. Ottimo articolo! Come sempre apprezzo molto l’attenzione che presti ai dettagli e questo articolo sara’ sicuramente utile a chiunque, affrontando il tuo stesso problema, si trovera’ a cercare qualche soluzione, e qui potra’ trovare tutti i motivi che ti hanno portato a scegliere questa strada!
    Anche io sviluppando app per la concorrenza (:P) Android mi sono trovato ad affrontare questo stesso problema, ma Google mette a disposizione delle API per gestire un oggetto chiamato SharedPreferences.
    Non conosco a fondo il mondo in cui questo oggetto si comporta, ma rende possibile mantenere le proprie “preferenze” anche in caso di aggiornamento di un app (o cancellazione e reinstallazione della stessa).
    Chissa’ se per iOS esiste qualcosa di vagamente simile….
    In bocca al lupo con le tue ricette 😉

    1. Grazie mille Armix, sempre molto gentile, apprezzo molto i tuoi commenti!
      Nel caso di preferenze (ma solo preferenze, il caso che di cui ho scritto è più generale) su IOS vi è la directory Application_Home/Library/Preferences.

      Questa directory contiene i file delle preferenze della specifica applicazione. Non dovremo mai creare file di preferenze direttamente, ma come su Android dovremo invece sfruttare la classe apposita “NSUserDefaults” o usere le “CFPreferences API” per leggere e settare le preferenze di un’applicazione. Il contenuto di questa directory viene “backuppato” da iTunes.

  2. http://bccm.pl/transport-pieniedzy
    Buying a new or used car could be a tough process unless you know what you will be carrying out. By educating yourself about auto purchasing prior to deciding to visit the dealership, you could make things less difficult for yourself. The following advice will help your following buying journey become more enjoyable.

    Usually deliver a auto technician together when shopping for a brand new motor vehicle. Auto retailers are popular for offering lemons and you may not need to be their up coming target. When you can not obtain a auto technician to think about autos along, a minimum of be sure that you have him look at your closing option before you purchase it.

    Know your restrictions. Before starting purchasing for your upcoming auto or van, decide how much you can manage to spend, and follow it. Don’t forget to incorporate fascination with your estimations. You will probably pay out about 20 percent as a down payment at the same time, so be well prepared.

    Before visiting a dealership, know which kind of automobile you want. Research all of you options prior to buying so that you can determine what works for your financial budget and family requires. Seek information to find out how much you ought to pay for a potential auto.

    Prior to signing any commitment make time to study every single range, like the fine print. When there is something listed that you do not recognize, will not sign until you get an response that you simply understand. Unsavory salesmen can make use of a contract to place several service fees that have been not mentioned.

    If you maintain the preceding advice at heart next time which you go purchasing a automobile, you may be very likely to obtain a good bargain. Purchasing a automobile does not have to become headache. Use the ideas from this article and you will obtain the auto you desire in a good cost.

Leave a Reply to Paul Cancel reply

Your email address will not be published. Required fields are marked *