La gestione di file è una caratteristica di primaria importanza nelle applicazioni che intendono salvare il proprio stato o esportare i loro contenuti (generalmente in forma testuale).
A causa delle restrizioni imposte per motivi di sicurezza sull’ iPhoneOS non è possibile vedere l’intero filesystem ma solo ciò che è all’interno del filesystem riservato alla vostra applicazione (sandbox), ciò porta all’impossibilità di usufruire direttamente dei dati utente (musica, video) o delle altre applicazioni.
Innanzitutto è importante conoscere il contenuto della home-directory comune a tutte le applicazioni in modo da tenere “in ordine” i file della nostra app…
L’home-directory è composta generalmente da quattro principali directory:
- NomeApplicazione: ha il nome dell’applicazione e contiene eseguibile, NIB, localizzazioni, ecc… (non potete effettuare operazioni di I/O)
- “Library”: è la cartella genitore di “Preferences” e la lettura/scrittura avviene attraverso l’uso delle API
- “Documents”: di default è vuota ed è gestibile a proprio piacimento
- “tmp”: come suggerisce il nome è la cartella dei file temporanei, potrete utilizzarla a vostro piacimento ma è importante ricordare che non viene presa in considerazione da iTunes e che bisogna ricordarsi di svuotarne il contenuto (buona norma è svutarlo alla chiusura dell’app)
Un consiglio di particolare importanza è quello di lasciar perdere le prime due directory e utilizzare solo le altre…
Ora passiamo a parlare un po più nella pratica di come potrà avvenire l’accesso al filesystem; per lo scopo utilizzeremo la classe NSFileManager (per maggiori info vi rimando alla documentazione ufficiale).
Visualizzare il contenuto di una directory
Per lo scopo useremo la classe NSFileManager e il suo metodo directoryContentsAtPath:
Questo ha come ritorno la lista dei nomi di file e directory nel percorso dato, questi sono restituiti all’interno di un NSArray di NSString.
Il consiglio è di utilizzare una UITableView che avrà come lunghezza la dimensione dell’array e come contenuto l’array stesso (i-esima cella => i-esimo elemento dell’NSArray).
Attributi di un file
Precedentemente abbiamo visto come ottenere il contenuto di una precisa directory ma, come avrete sicuramente notato, file e cartelle non sono distinguibili in quanto rappresentate entrambe una una NSString contenente il loro nome. Appunto per questo ricorreremo al metodo fileExistsAtPath:isDirectory: (sempre di NSFileManager), isDirectory è un puntatore a un boolean (BOOL) che avrà i valori YES se il percorso è una directory.
Lettura di un file
Per la lettura di un file ci rifaremo al concetto di flusso che considera i byte del file all’interno di uno stream che viene sequenzialmente letto (in questo modo non dovremmo necessariamente caricare tutto il file in memoria e perciò avremo evitato di dover leggere solo file di piccole dimensioni).
La classe che andremo ad utilizzare sarà una sottoclasse di NSStream denominata NSInputStream.
Possiamo riassumere le operazioni da eseguire in:
- creare un buffer di memoria contenente i byte letti dal flusso (stream);
- leggere ciclicamente (es. con un ciclio while) i blocchi di byte (con dimensione inferiore alla dimensione del flusso);
- operare sui byte (per esempio scriverli di seguito in un UITextView dopo averli opportunamente convertiti in stringhe);
- chiudere il flusso quando i byte sono terminati.
L’esempio del terzo punto è proprio quello che andremo ad affrontare…
Come riportiamo i byte nel UITextView ?
Penso che un codice esprima molto più di 1000 parole:
-(void) appendText: (NSString*) text {
textView.text = [NSString stringWithFormat: @"%@%@", textView.text, text];
}
La stringa in rosso rappresenta ciò che dobbiamo aggiungere mentre textView.text è il testo dell’UITextView.
Naturalmente non possiamo dare in input al metodo direttamente il blocco di byte letto ma dobbiamo prima convertirlo, anche in questo caso riporteremo la parte di codice necessria all’operazione:
NSString *bufferString = [[NSString alloc] initWithBytesNoCopy:buffer length:bufferLength encoding:NSUTF8StringEncoding freeWhenDone: NO];
[self appendText:bufferString];
“bufferString” sarà il testo in input ad appendText (che nel metodo in questione chiamiamo semplicemente text) mentre buffer sarà il gruppo di byte letti.
NSUTF8StringEncoding è utilizzabile solo per file di testo semplici.
Creazione e Scrittura di un file
Per la creazione del file useremo createFileAtPath: di NSFileManager.
Per la scrittura di questo useremo il concetto di flusso ma invece che considerarlo in input sarà in output (cioè verso il file creato); per questo scopo useremo il metodo write:maxLength: di NSOutputStream (sottoclasse di NSStream).
-(void) createAndSave {
[asyncOutputStream release];
[outData release];
outData = [[textView.text dataUsingEncoding: NSUTF8StringEncoding] retain];
outRange.location = 0;
NSString *newFile [parentDirectoryPath
stringByAppendingPathComponent: fileName.text];
[[NSFileManager defaultManager] createFileAtPath:newFile contents:nil attributes:nil];
asyncOutputStream = [[NSOutputStream alloc] initToFileAtPath: newFIle append: NO];
[asyncOutpuntStream setDelegate: self];
[asyncOutpuntStream scheduleInRunLoop:[NSRunLoop runLoop] forMode: NSDefaultRunLoopMode];
[asyncOutpuntStream open];
}
textView sarà UITextView che conterrà il testo del file
fileName sarà UITextField che conterrà il nome del file
asyncOutputStream indica che il flusso dei dati avviene in maniera asincrona (ciò può avvenire anche nel caso della lettura); in questo modo l’app non resta in blocco per tutta la durata del flusso ma opera solo quando il flusso ha dei byte disponibili.
outData conterrà il testo dell’UITextView convertito in NSData in modo da essere compatibile con il flusso
Nel file header (.h) saranno necessarie le variabili d’istanza:
NSData *outData;
NSRange *outRange;
NSOutputStream *asyncOutputStream;
Queste servono per coordinare il metodo e il callback…
E’ importante ricordare che la scrittura nel flusso genererà degli eventi, i più importanti sono:
- NSStreamEventHasSpaceAvaible: indica che il flusso è pronto
- NSStreamEventErrorOccurred: indica che si è verificato un errore (potete ad esempio visualizzare un UIAlertView)
Quando il primo caso si verificherà siamo pronti a impostare il buffer da passare a write:maxLength di NSOutputStream
[outputStream write: buffer maxLength: 1]
in questo caso la lunghezza è pari a 1 ma può essere modificata.
outputStream è il nostro NSOuputStream:
NSOutputStream *outputStream = (NSOutputStream*) stream;
Mentre stream è un NSStream* in input:
-(void)stream:(NSStream*)stream handelEvent:(NSStreamEvent)event { ... }
Buona programmazione a tutti e arrivederci al prossimo articolo…
Cinaglia Pietro









8 Responses to “Gestione dei file nelle nostre applicazione iPhone”
11 Luglio 2010
iPhone SDK: gestione files @ Informatica[…] SDK: gestione files Categoria: Programmazione, iPhone SDK — Admin @ 14:31 Leggi tutto l’articolo Segnalibri: sociallist_d8f51366_url = 'http://www.unicz.net/blog/?p=1160'; […]
26 Luglio 2010
Unknown Action « Panoramica sul “File System Locale” delle nostre applicazioni iPhone (iOS) | devAPP[…] giorno fa vi abbiamo proposto un tutorial sulla gestione dei File all’interno delle nostre applicazioni iPhone, vediamo oggi di approfondire l’argomento […]
24 Novembre 2010
LucaE’ possibile vedere la parte della creazione dei flussi? nella scrittura viene saltata… 😐
16 Dicembre 2010
GiuseppePotresti fare un esempio di lettura e salvataggio di file diversi da testo? Ad esempio una immagine?
Oppure sai dove posso trovare un articolo che lo spieghi?
La mia necessità è salvare in locale una immagine jpeg presa su internet.
Ciao
Giuseppe
19 Dicembre 2010
iMorphNella guida ufficiale ci sono alcuni esempi, esattamente per ciò che interessa a te, nella sezione riguardante la libreria NSUrlConnection… ti consiglio di scaricare “SimpleURLConnections” che fa esempi tu GET, PUT, POST se guardi il codice GET non fa altro che scaricare una immagine da internet in locale: http://developer.apple.com/library/ios/#samplecode/SimpleURLConnections/Introduction/Intro.html
ciao ciao
28 Giugno 2011
AngeloCiao, è possibile usare il tuo tutorial per salvare la posizione di un UISwitch per ritrovarlo nella stessa posizione in cui era stato lasciato, al riavvio dell’applicazione?
28 Giugno 2011
AngeloCiao, se volessi salvare la posizione di uno switch cosa devo fare? o puoi indirizzarmi a un tutorial dedicato.
Grazie mille
29 Giugno 2011
PietroIl tutorial è concentrato principalmente sulla gestione files; per salvare la configurazione della tua app (in questo caso il valore dell’UISwitch: aperto, chiuso) ti consiglio di usare un database SQLite, con la funzione cerca (chiave “SQLite”) trovi diversi tutorial validi con esempi molto utili 😉
ciao ciao