In questo articolo realizzeremo un'applicazione iOS per interagire con i servizi di cloud computing offerti dal servizio di storage online Dropbox. Vedremo come caricare file in the cloud, come leggerli, come cercarli, come cancellarli e così via. Prima di entrare nel vivo dell'articolo, è bene analizzare il contesto in cui la nostra applicazione si colloca, analizzando velocemente i temi che andremo ad affrontare.
Una premessa: l'articolo è pensato per un utilizzo pratico. Faremo dunque riferimento a istruzioni passo per passo su come creare ogni singolo componente della nostra applicazione. Per questo è necessario avere almeno le basi di programmazione per iOS, basi che è facile acquisire leggendo le prime lezioni della nostra guida alla creazione di applicazioni per iPhone e iPad. Tutti i file che utilizzeremo saranno scaricabili liberamente attraverso il link a fondo articolo.
Cloud computing e Dropbox
Con cloud computing identifichiamo, con un po' di approssimazione, un insieme di funzionalità fornite online che ci permettono di salvare, recuperare ed elaborare dati. Uno dei maggiori vantaggi è rappresentato dalla disponibilità delle risorse: si può accedere a dati e applicazioni da qualsiasi luogo poiché essi sono conservati nei server del provider e non sulla macchina del cliente. Un banale esempio di servizio fornito in cloud computing è quello delle webmail (tipo Gmail): accessibili da qualsiasi dispositivo con un semplice browser.
Tra i servizi di cloud computing, uno dei più diffusi è quello della condivisione e sincronizzazione di file su storage online. Tra questi servizi, Dropbox è sicuramente il più famoso, e meritatamente.
Ai fini del nostro scopo, la funzionalità che ci tornerà più utile è il suo essere multipiattaforma: Dropbox offre l'accesso ai contenuti tramite browser, ma soprattutto mette a disposizione un'applicazione per i più diffusi sistemi operativi sia desktop, sia mobile. In questo modo i nostri file non saranno solo accessibili via web, ma, una volta acceso il nostro computer o il nostro smartphone, potranno essere sincronizzati online.
Poiché utilizzeremo per la nostra applicazione le API di Dropbox, per il prosieguo dell'articolo è necessario avere un account attivo su questo servizio.
L'applicazione iOS demo
L'applicazione iOS che realizzeremo permetterà la fruizione e la modifica delle risorse presenti in una cartella Dropbox. L'applicazione sarà formata delle seguenti tre pagine:
- Una pagina di login per sfruttare l'applicazione Dropbox nativa ed eseguire il login con il nostro account.
- Una pagina in cui sarà possibile recuperare la lista dei file presenti in una cartella, creare una nuova cartella, cancellare un file o una cartella, scaricare un documento, effettuare l'upload di un file ed eseguire una ricerca all'interno di una cartella.
- Una pagina, contenete un oggetto di tipo
UIWebView
(componente di sistema che consente la visualizzazione di risorse web, immagini, files pdf e molto altro), che ci consentirà di mostrare un file precedentemente scaricato.
Registrare l'app su Dropbox
La prima operazione da fare è registrare la nostra applicazione in Dropbox. Per far ciò andiamo sulla home page per sviluppatori Dropbox e, nel menu di sinistra della pagina, facciamo sulla voce My apps. Se è la prima volta che accedete alla sezione, vi sarà chiesto di accettare i termini della licenza. Una volta attivi, all'interno di questa pagina potremo sia creare una nuova applicazione, sia gestire le applicazioni già esistenti.
Creiamo dunque la nostra applicazione cliccando sul bottone Create an App:
Si aprirà una pop-up come quella rappresentata nella figura sotto:
Tutti i campi mostrati nel form di registrazione sono obbligatori:
- App name: è il nome dell'applicazione.
- Description: è la descrizione associata all'applicazione che stiamo creando.
- Access Level: è il tipo di accesso che viene consentito all'applicazione.
Su quest'ultima opzione: se si spunta la prima opzione (App folder), l'applicazione avrà accesso esclusivamente ad una cartella (che verrà creata in automatico con il nome dell'applicazione) collegata all'utente che abbiamo utilizzato per creare l'applicazione. Se invece si spunta la seconda opzione (Full Dropbox) si permette l'accesso a tutte le cartelle associate all'account. Scegliamo questa seconda opzione perché consentirà di lavorare su cartelle condivise tra utenti, a differenza della prima che lavora solamente su una specifica cartella che non può essere condivisa con altri utenti.
Cliccando su Create viene creata l'applicazione e, dopo qualche secondo, si viene portati alla pagina web relativa all'applicazione stessa. Come possiamo vedere la pagina è composta da diverse sezioni; analizziamo le principali.
HTMLCloud
All'interno di questa sezione vengono riportati il nome dell'applicazione, il tipo di accesso che abbiamo impostato ed inoltre lo stato dell'applicazione. Quest'ultimo campo, come possiamo vedere dalla figura sopra, riporta il valore development; nel caso in cui si voglia rendere pubblica tale applicazione sarà necessario inviare una richiesta di pubblicazione.
Andando oltre possiamo vedere che sono presenti altri due campi (AppKey e AppSecret), fondamentali nello sviluppo dell'applicazione iOS per stabilire una connessione con i servizi Dropbox.
Additional users
Figura 04. Sezione Additional users relativa alla pagina di un'applicazione Dropbox
(clic per ingrandire)
L'utilizzo dell'applicazione, all'atto della sua creazione, viene consentito solo all'account con cui è stata creata. Ciò vuol dire che non sarà possibile utilizzarla se eseguiremo il login utilizzando un altro utente. Per rendere l'applicazione multi-utente è sufficiente cliccare sul link Enable multi users e, nella finestra pop-up, che apparirà confermare con un clic sul bottone Enable (figura sotto).
Durante il processo di sviluppo è consentito l'utilizzo simultaneo dell'applicazione solo a cinque account Dropbox differenti.
A questo punto la configurazione della nostra prima applicazione Dropbox è conclusa. L'ultima operazione che è necessario eseguire è la creazione di una cartella con nome HTMLCloud (non necessariamente deve avere lo stesso nome dell'applicazione Dropbox) nella root della gerarchia di cartelle dell'account Dropbox e condividere tale cartella con gli altri account che useremo per eseguire lo sviluppo dell'applicazione.
SDK di Dropbox e Xcode
Terminate le operazioni preliminari, siamo pronti ad integrare all'interno di un nostro progetto Xcode l'SDK di Dropbox (per le basi della programmazione iOS e l'uso di Xcode si rimanda alla nostra guida alla creazione di applicazioni per iPhone e iPad).
Andiamo dunque alla pagina dei download nella sezione developer di Dropbox ed ovviamente, tra gli SDK offerti, selezioniamo quello relativo ad iOS. Terminato il download del file in formato zip, decoprimiamolo e, al suo interno, identifichiamo la cartella DropboxSDK.framework che rappresenta l'SDK di sviluppo ed è quella che andremo ad importare nel nostro progetto Xcode.
Apriamo dunque l'IDE di sviluppo, scegliamo come template di partenza Empty Application e creiamo un nuovo progetto come segue:
Scegliamo come nome HTMLCloud, come Device Family scegliamo iPhone ed infine spuntiamo il check box relativo all'Automatic Reference Count, il quale ci assolverà da dover inserite chiamate splicite ai metodi release o retain sui vari oggetti.
A questo punto torniamo all'interno della cartella relativa all'SDK di Dropbox precedentemente scaricata e, con un drag-and-drop del mouse, importiamo la cartella DropboxSDK.framework all'interno del progetto (nella schermata che apparirà dopo il drop del framework spuntare l'opzione Copy items into destination group's folder).
C'è da notare subito una cosa: se provassimo ad effettuare ora un build del progetto otterremo diversi errori. Questo perché il framework Dropbox utilizza altri due framework iOS: QuartzCore.framework e Security.framework. Per aggiungere un nuovo framework al progetto clicchiamo sulla root dell'albero di navigazione del progetto nella parte sinistra di Xcode, selezioniamo HTMLCloud come Target ed infine sul tab Build phases.
Esploriamo dunque la sezione Link Binary With Libraries e clicchiamo sul tasto "+". Nella nuova finestra che compare selezioniamo uno dei due framework necessari che abbiamo elencato precedentemente e clicchiamo su Add. Ripetendo l'operazione anche per il secondo framework mancante, la configurazione finale dell'albero di navigazione del progetto sarà la seguente:
Il lavoro di importazione e configurazione iniziale del framework di Dropbox è terminato. Effettuando adesso un Build del progetto l'operazione verrà eseguita senza nessun problema.
Creare la pagina di Login
Siamo pronti per sviluppare la nostra applicazione. In questa lezione andremo a realizzare la pagina iniziale che consentirà le seguenti operazioni:
- Login/Logout al servizio di Dropbox.
- Accesso alla cartella HTMLCloud dell'applicazione.
Spostiamo subito l'attenzione all'interno del file AppDelegate.m, il file che rappresenta il punto iniziale del flusso d'esecuzione dell'applicazione. Subito dopo le direttive di import
definiamo le seguenti due costanti:
#import <DropboxSDK/DropboxSDK.h>
#define APPKEY @"2sjagz8grla0nyi"
#define APPSECRET @"xx7e1fmvq6g8t28"
I valori relativi ad APPKEY
e APPSECRET
sono naturalmente gli stessi che abbiamo visto nella precedente lezione quando abbiamo creato l'applicazione nella sezione developer di Dropbox. Ovviamente assicuratevi che i valori inseriti siano corretti, altrimenti non sarà possibile instaurare una connessione con i servizi Dropbox.
Adesso spostiamoci nel metodo application: didFinishLaunchingWithOptions:
e, dopo la definizione del frame
della window
, inseriamo le seguenti linee di codice:
DBSession* dbSession = [[DBSession alloc] initWithAppKey:APPKEY appSecret:APPSECRET root:kDBRootDropbox];
[DBSession setSharedSession:dbSession];
La classe DBSession
è la prima classe dell'SDK di Dropbox che introdurremo. Questa classe permette l'avvio di una sessione con i servizi di Dropbox effettuando un controllo sui valori di APPKEY
e APPSECRET
forniti dallo sviluppatore. Vedremo più avanti in questa lezione come usare questa sessione per effettuare il login con un utente.
Abbandoniamo adesso il file AppDelegate.m e, dato che abbiamo scelto un progetto di tipo Empty Application, avremo, nella nostra gerarchia di file e cartelle di progetto, solo il file AppDelegate. Aggiungiamo dunque un nuovo file, scegliamo come template UIViewController subclass (che in automatico inseririrà, nella definizione della nuova classe, la derivazione dalla classe UIViewController) e chiamiamolo MenuViewController.
Spostiamoci poi nel file MenuViewController.h e creiamo ("dichiariamo", in linguaggio di programmazione) due bottoni che svolgeranno l'operazione di login/logout e l'accesso alla cartella condivisa:
UIButton *_linkButton;
UIButton *_enterButton;
A questo punto spostiamoci nel file MenuViewController.m e, come prima cosa, importiamo l'SDK di Dropbox come segue:
#import <DropboxSDK/DropboxSDK.h>
Infine dichiariamo il seguente metodo di init
:
- (id)init
{
self = [super init];
if (self) {
self.view.backgroundColor = [UIColor whiteColor];
UIImageView *htmlLogo = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"logoHTML.jpg"]];
[self.view addSubview:htmlLogo];
_linkButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
_linkButton.frame = CGRectMake(10, 150, 300, 60);
[_linkButton setTitle:@"Accedi con account Dropbox" forState:UIControlStateNormal];
[_linkButton addTarget:self action:@selector(didPressLink) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:_linkButton];
_enterButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
_enterButton.frame = CGRectMake(10, 250, 300, 60);
[_enterButton setTitle:@"Entra nella cartella dell'applicazione" forState:UIControlStateNormal];
[_enterButton addTarget:self action:@selector(showDropboxFolder) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:_enterButton];
[self updateButtonsLabelWithState:[[DBSession sharedSession] isLinked]];
}
return self;
}
Nel metodo di init
creiamo semplicemente un'immagine (contenente il logo di HTML.it) e i due pulsanti che svolgeranno le funzioni viste precedentemente.
Da notare, nell'ultima riga di codice, l'accesso alla propertyisLinked
del DBSession
. Tale property restituisce un valore booleano: YES
se è già stato eseguito il login con un account Dropbox e NO
se ciò non è avvenuto.
Nel codice indicato sopra abbiamo inoltre associato al bottone _linkButton
il metodo didPressLink
che si occuperà di svolgere la funzione di login/logout e al bottone _enterButton
il metodo showDropboxFolder
che consentirà l'accesso alla cartella condivisa Dropbox.
Per dichiararli, sempre nel file MenuViewController.m, prima della direttiva @implementation MenuViewController
, implementiamo i seguenti metodi privati:
@interface MenuViewController(private)
- (void)showDropboxFolder;
- (void)didPressLink;
@end
@implementation MenuViewController(private)
- (void)showDropboxFolder
{
}
- (void)didPressLink
{
if (![[DBSession sharedSession] isLinked]) {
[[DBSession sharedSession] linkFromController:self];
}
else {
[[DBSession sharedSession] unlinkAll];
[self updateButtonsLabelWithState:NO];
}
}
@end
Il metodo showDropboxFolder
verrà completato nella prossima lezione, mentre vale la pena analizzare da subito il codice all'intero del metodo didPressLink
.
Prima di tutto, per differenziare l'operazione di login da quella di logout, utilizziamo la property isLinked
della classe DBSession
. Per effettuare il login richiamiamo il metodo linkFromController:
che eseguirà la seguente operazione: se è installata sul dispositivo, verrà richiamata l'applicazione Dropbox per permettere il login al suo interno; altrimenti verrà aperta una pagina web all'interno della nostra stessa applicazione per consentire la medesima operazione. Per disconnettere l'utente utilizziamo il metodo unlinkAll
.
A questo punto la scrittura del MenuViewController è quasi terminata: ci rimane solo la dichiarazione del metodo updateButtonsLabelWithState:
che si occupa semplicemente di cambiare il testo contenuto nel bottone _linkButton
. Andiamo dunque nel file MenuViewController.h ed inseriamo la seguente linea di codice:
- (void)updateButtonsLabelWithState:(BOOL)linked;
Spostiamoci poi nel metodo MenuViewController.m ed implementiamo il metodo come segue:
- (void)updateButtonsLabelWithState:(BOOL)linked
{
if (linked) {
_enterButton.enabled = YES;
[_enterButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[_linkButton setTitle:@"Disconnetti account Dropbox" forState:UIControlStateNormal];
[_linkButton setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
}
else {
_enterButton.enabled = NO;
[_enterButton setTitleColor:[UIColor lightGrayColor] forState:UIControlStateNormal];
[_linkButton setTitle:@"Accedi con account Dropbox" forState:UIControlStateNormal];
[_linkButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
}
}
A questo punto il lavoro sulla classe MenuViewController
è terminato. Spostiamoci adesso nel file AppDelegate.h nel quale andremo a dichiarare un oggetto di tipo MenuViewController
.
Per prima cosa, importiamo il file MenuViewController.h. Dichiariamo poi un oggetto di tipo MenuViewController
come segue:
MenuViewController *_menuVC;
Spostiamoci nel file AppDelegate.m e nel metodo application:didFinishLaunchingWithOptions:
inseriamo il seguente codice dopo la dichiarazione del DBSession
:
MenuViewController * menuVC = [[MenuViewController alloc] init];
self.window.rootViewController = menuVC;
Ultima modifica da apportare al file AppDelegate.m è l'inserimento del seguente metodo:
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
if ([[DBSession sharedSession] handleOpenURL:url]) {
if ([[DBSession sharedSession] isLinked]) {
[_menuVC updateButtonsLabelWithState:YES];
}
return YES;
}
return NO;
}
Tale metodo verrà richiamato una volta terminato il login all'interno dell'applicazione Dropbox o tramite la pagina web (mostrata nel caso in cui sul dispositivo non sia installata l'app di Dropbox).
Adesso è necessario eseguire l'ultima, ma fondamentale, configurazione al nostro progetto. Selezioniamo il file HTMLCloud-Info.plist
e, con un clic destro, selezioniamo Open As ed infine clicchiamo su Source Code:
Verrà mostrato HTMLCloud-Info.plist in una sintassi XML. Dopo il tag <dict>
incolliamo il seguente codice:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>db-2sjagz8grla0nyi</string>
</array>
</dict>
</array>
Questa direttiva è necessaria per consentire il login tramite l'applicazione Dropbox nativa e richiamare nuovamente la nostra applicazione una volta terminato il login (naturalmente nel campo <string>
dovete sostituire l'APPKEY di esempio con quello della vostra applicazione Dropbox).
A questo punto siamo pronti per effettuare il primo Run del progetto. La schermata di login sarà la seguente:
Se tocchiamo il pulsante Accedi con account Dropbox, la nostra applicazione andrà in background e comparirà l'applicazione Dropbox (se installata) come nella seguente figura (operazione permessa grazie alla direttiva inserita all'interno del file HTMLCloud-Info.plist):
Tocchiamo dunque il tasto Allow
e, dopo qualche istante, vedremo ricomparire la nostra applicazione (operazione permessa sempre grazie alla direttiva inserita all'interno del file HTMLCloud-Info.plist) e, se il login è andato a buon fine, la schermata principale apparirà nel seguente modo:
Recupero dei file da Dropbox: l'oggetto DBRestClient
Entriamo nel vivo della trattazione andando a mostrare il codice necessario per acquisire la lista di file presente all'interno di una cartella Dropbox.
In questa lezione andremo ad introdurre la classe più importante dell'SDK di Dropbox e vedremo,
utilizzando i metodi di delegato associati a questa classe, come effettuare le chiamate ai servizi di
Dropbox e gestire le informazioni che ci vengono ritornate come risposta alla nostra chiamata.
Come prima cosa creiamo un nuovo file (scegliendo sempre come template UIViewController subclass) e nominiamolo HierarchyViewController.
Spostiamoci nel file HierarchyViewController.h e dichiariamo il seguente bottone come attributo di classe:
UIButton *_showFilesListButton;
Spostiamoci nel file HierarchyViewController.m
e dichiariamo il seguente metodo di init
con al suo interno la dichiarazione del bottone precedentemente dichiarato:
- (id)init{
self = [super init];
if (self) {
self.view.backgroundColor = [UIColor whiteColor];
_showFilesListButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
_showFilesListButton.frame = CGRectMake(10, 30, 300, 40);
[_showFilesListButton setTitle:@"Lista files" forState:UIControlStateNormal];
[_showFilesListButton addTarget:self action:@selector(didPressShowFiles) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:_showFilesListButton];
}
}
A questo punto è necessario dichiarare il metodo didPressShowFiles
come metodo privato (ovviamente lo si può dichiarare anche come metodo pubblico) andando ad inserire il seguente codice prima della direttiva @implementation HierarchyViewController
:
@interface HierarchyViewController(private)
- (void)didPressShowFiles;
@end
@implementation HierarchyViewController(private)
- (void)didPressShowFiles{
}
@end
Arrivati a questo punto dovremmo implementare il metodo didPressShowFiles
, ma lo faremo dopo perché è arrivato il momento di introdurre la classe che gestisce tutte le chiamate con i servizi di Dropbox: il DBRestClient.
Tale classe provvede ad effettuare chiamate di tipo asincrono ai servizi di Dropbox e permette allo sviluppatore di conoscere l'esito della chiamata semplicemente implementando il protocollo DBRestClientDelegate. Questo protocollo, per ogni metodo (ossia per ogni azione sulla nostra cartella condivisa su Dropbox) mette a disposizione due metodi: uno che viene richiamato se l'operazione è andata a buon fine e un altro che viene richiamato se l'operazione non è andata a buon fine e si è dunque verificato un errore. Solo nei metodi di upload e di download abbiamo a disposizione un terzo metodo di delegato che ci consente di conoscere lo stato di avanzamento relativo al download o all'upload
Fatta questa breve introduzione, andiamo a mostrare il codice necessario per scaricare la lista di elementi presenti nella cartella HTMLCloud. Spostiamoci nel file HierarchyViewController.h ed implementiamo il protocollo DBRestClientDelegate
come segue (ovviamente prima inseriamo la seguente linea di codice: #import <DropboxSDK/DropboxSDK.h>
)
@interface HierarchyViewController : UIViewController <DBRestClientDelegate>
Sempre nel file HierarchyViewController.h dichiariamo il seguente attributo:
DBRestClient *_restClient;
A questo punto spostiamoci nel file HierarchyViewController.m ed aggiungiamo le seguenti linee di codice nel metodo di init
(inserito precedentemente) prima della dichiarazione del _showFilesListButton
:
_showFilesListButton:
- (id)init {
_restClient = [[DBRestClient alloc] initWithSession:[DBSession sharedSession]];
_restClient.delegate = self;
}
Come possiamo vedere allochiamo l'oggetto _restClient
passando come parametro il DBSession
che abbiamo visto nella precedente lezione e indichiamo come delegato self
. Ciò ci consentirà di implementare i metodi di delegato direttamente nella classe HierarchyViewController.
Adesso nel metodo didPressShowFiles
, sempre nel file HierarchyViewController.m, inseriamo la seguente linea di codice:
[_restClient loadMetadata:@"/HTMLCloud"];
L'invocazione al metodo loadMetadata:
ci permetterà di acquisire la lista dei file contenuti all'interno del path passato come parametro (nel nostro caso passiamo come parametro la cartella HTMLCloud che avevamo creato e condiviso precedentemente).
A questo punto implementiamo, sempre nel file HierarchyViewController.m, i metodi di delegato di cui abbiamo bisogno:
- (void)restClient:(DBRestClient *)client loadedMetadata:(DBMetadata *)metadata
{
UIAlertView *av = [[UIAlertView alloc]initWithTitle:@"Lista files" message:@"Lista file acquisita con successo" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"ok", nil];
[av show];
NSLog(@"Folder '%@' contains:", metadata.path);
for (DBMetadata *file in metadata.contents) {
NSLog(@"%@", file.filename);
}
}
- (void)restClient:(DBRestClient*)client loadMetadataFailedWithError:(NSError*)error
{
UIAlertView *av = [[UIAlertView alloc]initWithTitle:@"Attenzione!" message:@"Impossibile acquisire la lista dei files" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"ok", nil];
[av show];
NSLog(@"%@", error);
}
Nel primo metodo (richiamato se non ci sono stati errori) viene restituito un oggetto di tipo DBMetadata
i cui attributi principali sono i seguenti:
filename
: rappresenta il nome del file o cartella.isDirectory
: ci informa se il file è una cartella o un file generico.contents
: è un array che al suo interno contiene oggetti di tipoDBMetadata
. In questo modo abbiamo a disposizione l'intera gerarchia di cartelle e file presenti nella cartella attualmente scaricata.
All'interno di questo metodo di delegato semplicemente stampiamo sulla console la lista di file e cartelle contenute all'interno della cartella HTMLCloud e mostriamo a schermo un avviso.
Il lavoro sullo HierarchyViewController.m è terminato. Torniamo nel file MenuViewController.m ed importiamo la classe HierarchyViewController
:
#import "HierarchyViewController.h"
Spostiamoci nel metodo showDropboxFolder
e popoliamolo con il seguente codice:
HierarchyViewController *hierarchyVC = [[HierarchyViewController alloc] init];
[self presentModalViewController:hierarchyVC animated:YES];
A questo punto eseguiamo un Run del progetto, premiamo il bottone per visualizzare il contenuto della cartella Dropbox e poi premiamo il pulsante Lista Files; vedremo comparire, sulla console, la lista dei file contenuti nella cartella HTMLCloud (i file senza estensione sono delle cartelle). .
Il percorso che abbiamo passato al metodo loadMetadata:
faceva riferimento alla nostra cartella root del progetto. Per esplorare sottocartelle presenti nella cartella root è sufficiente indicare il percorso nel path, come nel codice seguente:
[_restClient loadMetadata:@"/HTMLCloud/ApplicazioneDemo/Prova"];
Creazione di una nuova cartella
Vediamo come aggiungere una nuova cartella all'interno della cartella HTMLCloud. Per prima cosa torniamo nel file HierarchyViewController.h e creiamo un nuovo pulsante:
UIButton *_createFolderButton;
Spostiamoci nel file HierarchyViewController.m e inizializziamo il pulsante sempre nel metodo di init
:
_createFolderButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
_createFolderButton.frame = CGRectMake(10, 100, 300, 40);
[_createFolderButton setTitle:@"Crea Cartella" forState:UIControlStateNormal];
[_createFolderButton addTarget:self action:@selector(didPressCreateFolder) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:_createFolderButton];
A questo punto dichiariamo il metodo privato didPressCreateFolder
ed inseriamo la sua implementazione:
-(void)didPressCreateFolder{
NSString * pathDir = [NSString stringWithFormat:@"/HTMLCloud/MiaCartella/"];
NSString * folderName = [NSString stringWithFormat:@"NuovaCartella"];
[_restClient createFolder:[NSString stringWithFormat:@"%@%@", pathDir, folderName]];
}
Come possiamo vedere abbiamo creato due stringe: una rappresenta la directory nella quale verrà creata la nuova cartella (pathDir
), un'altra il nome della cartella che vogliamo creare (folderName
). Infine passiamo la concatenazione di queste due stringe al metodo createFolder:
.
Nel caso in cui il percorso espresso nella stringa pathDir
non esistesse (ossia, seguendo l'esempio, quando non esiste la cartella MiaCartella), tale percorso verrà creato automaticamente: verrà creata prima la cartella MiaCartella ed al suo interno la cartella NuovaCartella.
Adesso implementiamo, sempre nella classe HierarchyViewController.m, i seguenti metodi di delegato per conoscere l'esito della chiamata ai servizi di Dropbox:
- (void)restClient:(DBRestClient *)client createdFolder:(DBMetadata *)folder
{
UIAlertView *av = [[UIAlertView alloc]initWithTitle:@"Creazione cartella" message:@"Creazione cartella eseguita con successo!" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"ok", nil];
[av show];
}
- (void)restClient:(DBRestClient *)client createFolderFailedWithError:(NSError *)error
{
UIAlertView *av = [[UIAlertView alloc]initWithTitle:@"Attenzione!" message:@"Cartella già presente" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"ok", nil];
[av show];
NSLog(@"%@", error);
}
Nel caso in cui sia già presente una cartella con lo stesso nome della cartella che si vuole creare verrà richiamato il metodo di delegato restClient: createFolderFailedWithError:
.
Cancellazione di un file o una cartella
Vediamo adesso come cancellare un file o una cartella presente sulla nostra cartella condivisa Dropbox. Come al solito, apriamo il file HierarchyViewController.h e dichiariamo un nuovo bottone:
UIButton *_deleteFolderOrFileButton;
Nel file HierarchyViewController.m inseriamo la dichiarazione del nuovo pulsante, sempre nel metodo di init
:
_deleteFolderOrFileButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
_deleteFolderOrFileButton.frame = CGRectMake(10, 180, 300, 40);
[_deleteFolderOrFileButton setTitle:@"Cancella cartella o file" forState:UIControlStateNormal];
[_deleteFolderOrFileaddTarget:self action:@selector(didPressDeleteButton) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:_deleteFolderOrFileButton];
Dichiariamo il metodo privato didPressDeleteButton
ed implementiamolo come segue (sempre nel file HierarchyViewController.m):
NSString * pathDir = [NSString stringWithFormat:@"/HTMLCloud/MiaCartella/"];
NSString * filename = [NSString stringWithFormat:@"NuovaCartella"];
[_restClient deletePath:[NSString stringWithFormat:@"%@%@", pathDir, filename]];
Anche in questo caso è sufficiente una sola linea di codice per cancellare una risorsa (file o cartella) indicata dal path passato come parametro (effettuiamo la stessa concatenazione di stringe vista nella precedente lezione).
Come per le altre chiamate viste precedentemente, implementiamo i due metodi di delegato di cui abbiamo bisogno, per conoscere l'esito della chiamata ai servizi di Dropbox:
- (void)restClient:(DBRestClient *)client deletedPath:(NSString *)path
{
UIAlertView *av = [[UIAlertView alloc]initWithTitle:@"Cancellazione cartella" message:@"Cancellazione cartella eseguita con successo" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"ok", nil];
[av show];
NSLog(@"Deleted path %@", path);
}
- (void)restClient:(DBRestClient *)client deletePathFailedWithError:(NSError *)error
{
UIAlertView *av = [[UIAlertView alloc]initWithTitle:@"Attenzione!" message:@"Il file non esiste" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"ok", nil];
[av show];
NSLog(@"%@", error);
}
Ovviamente, nel caso in cui si richiedesse la cancellazione di una risorsa non presente nella cartella Dropbox, verrà richiamato il metodo di delegato restClient: deletePathFailedWithError:
che ci notificherà tale errore.
Download e visualizzazione di un documento
A questo punto possiamo abilitare l'applicazione al download, alla visualizzazione e all'upload di un documento presente nella nostra cartella condivisa Dropbox. In questa lezione ci occuperemo delle prime due operazioni, la terza la vedremo nella lezione successiva.
Nei prossimi paragrafi vedremo dunque come eseguire le seguenti operazioni:
- Download della risorsa utilizzando un metodo del
restClient.
- Definizione di una path nella cartella Documenti dove verrà salvata la risorsa.
- Visualizzazione del file all'interno di una
UIWebView
, oggetto di sistema che ci consente di visualizzare, in maniera molto semplice, oltre a pagine HTML anche file di estensione png, jpg, pdf e molto altro. - Cancellazione della risorsa dalla cartella Documenti una volta terminato il caricamento nella
UIWebView.
Come prima cosa abbiamo bisogno di un nuovo ViewController
che chiameremo DocumentViewController
. Andiamo nel file DocumentViewController.h ed inseriamo il seguente codice:
#import <DropboxSDK/DropboxSDK.h>
@interface DocumentViewController : UIViewController <DBRestClientDelegate,UIWebViewDelegate>{
UIWebView *_webView;
DBRestClient *_restClient;
NSString *_filePath;
}
- (void)back;
Spostiamoci adesso nel file DocumentViewController.m ed inseriamo il seguente codice:
- (id)init
{
self = [super init];
if (self) {
self.view.backgroundColor = [UIColor whiteColor];
UIToolbar * _toolbar = [[UIToolbar alloc]initWithFrame:CGRectMake(0, 0, 320, 44)];
_toolbar.barStyle = UIBarStyleBlack;
[self.view addSubview:_toolbar];
UIBarButtonItem * item = [[UIBarButtonItem alloc]initWithTitle:@"Indietro" style:UIBarButtonItemStyleBordered target:self action:@selector(back)];
_toolbar.items = [NSArray arrayWithObject:item];
_webView = [[UIWebView alloc] initWithFrame: CGRectMake(0, 44, 320, 480)];
_webView.delegate = self;
[self.view addSubview:_webView];
_restClient = [[DBRestClient alloc] initWithSession:[DBSession sharedSession]];
_restClient.delegate = self;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
_filePath = [[NSString alloc] initWithString:[documentsDirectory stringByAppendingPathComponent:@"logoHTML.jpg"]];
NSString *dropboxPath = [NSString stringWithFormat:@"/HTMLCloud/logoHTML.jpg"];
[_restClient loadFile:dropboxPath intoPath:_filePath];
}
return self;
}
- (void)back{
[self dismissModalViewControllerAnimated:YES];
}
Concentriamo la nostra attenzione nella parte finale del metodo di init
(in quanto le prime linee fanno riferimento semplicemente all'allocazione di una UIToolbar
e di un UIBarButtonItem
). Quello che facciamo è salvare il percorso relativo alla cartella Documenti in cui verrà copiata la risorsa scaricata (salvata nella variabile _filePath
) e richiamare il metodo loadFile: intoPath:
passando come parametri il percorso della risorsa relativa alla cartella Dropbox (dropboxPath
) e quello in cui verrà salvata la risorsa.
Il metodo back
viene utilizzato per tornare al menu principale dell'applicazione ed effettuare dunque il dismiss (ovvero la rimozione del ViewController dallo schermo) del DocumentViewController.
Vediamo come gestire il download della risorsa (sempre nel file DocumentViewController.m)
- (void)restClient:(DBRestClient *)client loadedFile:(NSString *)destPath
{
NSURL *url = [NSURL URLWithString:destPath];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[_webView loadRequest:request];
}
- (void)restClient:(DBRestClient *)client loadFileFailedWithError:(NSError *)error{
NSLog(@"%@", error);
}
- (void)restClient:(DBRestClient *)client loadProgress:(CGFloat)progress forFile:(NSString *)destPath
{
NSLog(@"%f", progress);
}
Nel metodo restClient: loadedFile:
la prima cosa che facciamo è creare un oggetto di tipo NSURL
(che è la classe, nell'SDK Apple, utilizzata per la gestione degli URL a risorse sia locali sia web) utilizzando la stringa destPath. Allochiamo poi un oggetto di tipo NSURLRequest
(ovvero la classe che consente di specificare oltre alla risorsa che vogliamo acquisire anche le ”regole” di acquisizione di tale risorsa) con l'NSURL
creato. Infine carichiamo all'interno della _webView
il contenuto della request precedentemente creata.
Come possiamo vedere, in questo caso abbiamo un terzo metodo di delegato: restClient: loadProgress: forFile:
. Tale metodo ci consente di conoscere l'avanzamento del download per mezzo della variabile progess
. Tale informazione è molto utile per far percepire all'utente il progresso dell'operazione per mezzo, ad esempio, di una UIProgressView
, ovvero una barra che mostra, per esempio, l'avanzamento di un download.
Ultima operazione che dobbiamo implementare è la cancellazione della risorsa dalla cartella Documenti. Per far ciò sfruttiamo un metodo di delegato dell'oggetto UIWebView
come segue:
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
NSFileManager *fileMgr = [NSFileManager defaultManager];
NSError *error;
[fileMgr removeItemAtPath:_filePath error:&error];
}
Concluso il lavoro sulla classe DocumentViewController
dobbiamo adesso tornare nel file HierarchyViewController.h e dichiarare un bottone a cui associare la funzionalità di download di una risorsa:
UIButton *_downloadDocumentButton;
Allochiamo il bottone nel metodo di init
fino a questo momento utilizzato nel file HierarchyViewController.m:
_downloadDocumentButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
_downloadDocumentButton.frame = CGRectMake(10, 260, 300, 40);
[_downloadDocumentButton setTitle:@"Download documento" forState:UIControlStateNormal];
[_downloadDocumentButton addTarget:self action:@selector(didPressDownloadFile) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:_downloadDocumentButton];
Dopo aver dichiarato il metodo didPressDownloadFile
implementiamolo inserendo il
seguente codice che allocherà e mostrerà a schermo un oggetto di tipo DocumentViewController
:
DocumentViewController * documentVC = [[DocumentViewController alloc]init];
documentVC.modalPresentationStyle = UIModalPresentationFullScreen;
documentVC.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:documentVC animated:YES];
Effettuando un Run del progetto e premendo il bottone Download File apparirà il nostro DocumentViewController
(la classe che andrà a mostrare la risorsa scaricata); dopo qualche secondo comparirà anche la risorsa che abbiamo deciso di scaricare (il tempo necessario alla visualizzazione è ovviamente proporzionale alla dimensione della risorsa scaricata):
Figura 14. DocumentViewController dopo aver visualizzato una risorsa presente nella cartella Dropbox
(clic per ingrandire)
Upload di un file
Per completare la gestione offline/online dei file, vediamo come eseguire l'upload di un file presente all'interno della nostra applicazione sui servizi online di Dropbox. Nulla vieta al lettore di estendere l'esempio che andremo a mostrare effettuando l'upload di un file, ad esempio, dal rullino fotografico.
Anche in questo caso dichiariamo un nuovo pulsante nel file HierarchyViewController.h:
UIButton *_uploadFileButton;
Dichiariamolo nel metodo di init
nel file HierarchyViewController.m:
_uploadFileButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
_uploadFileButton.frame = CGRectMake(10, 340, 300, 40);
[_uploadFileButton setTitle:@"Upload file" forState:UIControlStateNormal];
[_uploadFileButton addTarget:self action:@selector(didPressUploadFile) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:_uploadFileButton];
Dopo aver dichiarato, con il codice sopra, il metodo didPressUploadFile
inseriamo la sua implementazione (sempre nel file HierarchyViewController.m) come segue:
- (void)didPressUploadFile
{
NSString *localPath = [[NSBundle mainBundle] pathForResource:@"logoHTML" ofType:@"jpg"];
NSString *filename = @"logoHTML.jpg";
NSString *destDir = @"/HTMLCloud";
[_restClient uploadFile:filename toPath:destDir withParentRev:nil fromPath:localPath];
}
Come possiamo vedere, la prima cosa che facciamo è salvare il percorso locale relativo al file di cui vogliamo effettuare l'upload (localPath
). Dopo aver definito il nome del file (filename
) e il percorso in cui verrà copiato nella cartella condivisa Dropbox (destDir
), richiamiamo il metodo uploadFile: toPath: withParentRev: fromPath:
.
Adesso, come abbiamo fatto fino ad ora per ogni chiamata, aggiungiamo i metodi di delegato (sempre nel file HierarchyViewController.m):
- (void)restClient:(DBRestClient*)client uploadedFile:(NSString*)destPath from:(NSString*)srcPath metadata:(DBMetadata*)metadata {
UIAlertView *av = [[UIAlertView alloc]initWithTitle:@"Upload file" message:@"Upload del file eseguito con successo" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"ok", nil];
[av show];
NSLog(@"File uploaded successfully to path: %@", metadata.path);
}
- (void)restClient:(DBRestClient*)client uploadFileFailedWithError:(NSError*)error {
UIAlertView *av = [[UIAlertView alloc]initWithTitle:@"Attenzione!" message:@"Impossibile effettuare l'upload del file" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"ok", nil];
[av show];
NSLog(@"File upload failed with error - %@", error);
}
- (void)restClient:(DBRestClient *)client uploadProgress:(CGFloat)progress forFile:(NSString *)destPath from:(NSString *)srcPath
{
NSLog(@"Progress %f", progress);
}
Come possiamo vedere, anche in questo caso abbiamo a disposizione il metodo di delegato restClient: uploadProgress: forFile: from:
che ci fornisce l'avanzamento dell'upload del file.
Ricerca di un file
Per completare tutte le potenzialità di un sistema del genere, vediamo ora l'ultima funzionalità che abbiamo previsto nel nostro progetto, ossia la possibilità di ricercare, una volta specificata un percorso, un file o una cartella.
CoCome sempre andiamo nel file HierarchyViewController.h e dichiariamo un nuovo bottone:
UIButton *_searchFolderOrFileButton;
Andiamo adesso nel metodo di init
del file HierarchyViewController.m e dichiariamo il nuovo bottone:
_searchFolderOrFileButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
_searchFolderOrFileButton.frame = CGRectMake(10, 410, 300, 40);
[_searchFolderOrFileButton setTitle:@"Cerca file" forState:UIControlStateNormal];
[_searchFolderOrFileButton addTarget:self action:@selector(didPressSearchFolderORFile) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:_searchFolderOrFileButton];
Come sempre fatto fino ad ora, dichiariamo il metodo didPressSearchFolderORFile
associato al tap del bottone ed inseriamone la sua implementazione:
- (void)didPressSearchFolderORFile{
[_restClient searchPath:@"/HTMLCloud" forKeyword:@"HTML"];
}
Come possiamo vedere, anche in questo caso, è sufficiente una linea di codice per svolgere la funzione di ricerca. Il metodo searchPath: forKeyword:
accetta in ingresso due parametri:
searchPath
: rappresenta il percorso in cui verrà effettuata la ricerca. Ovviamente quest'ultima verrà estesa anche alle eventuali sottocartelle.keyword
: rappresenta la chiave di ricerca utilizzata. Sottolineiamo il fatto che la ricerca viene effettuata anche nell'estensione del file.
Inseriamo adesso i metodi di delegato:
- (void)restClient:(DBRestClient*)restClient loadedSearchResults:(NSArray *)results forPath:(NSString *)path keyword:(NSString *)keyword{
UIAlertView *av = [[UIAlertView alloc]initWithTitle:@"Ricerca" message:@"Ricerca terminata con successo" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"ok", nil];
[av show];
for (DBMetadata * metadata in results) {
NSLog(@"Nome file: %@", metadata.filename);
}
}
- (void)restClient:(DBRestClient *)restClient searchFailedWithError:(NSError *)error{
UIAlertView *av = [[UIAlertView alloc]initWithTitle:@"Attenzione!" message:@"Impossibile avviare la ricerca" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"ok", nil];
[av show];
}
Come possiamo vedere il metodo restClient: loadedSearchResults: forPath: keyword:
oltre al percorso e alla keyword utilizzata per la ricerca, restituisce l'array result
. Questo array contiene oggetti di tipo DBMetaData
, gli stessi oggetti che abbiamo incontrato quando abbiamo implementato la funzionalità di acquisizione della lista di file o cartelle presenti in un determinato percorso.
Effettuando un Run del progetto e premendo il tasto Cerca file, sulla console otterremo un risultato simile al seguente:
Interfaccia utente avanzata
Durante l'evento HTML.it Release party 2012 è stata mostrata una demo di un'applicazione iOS che offre all'utente un'interfaccia grafica completa per poter interagire con i servizi di Dropbox. Le funzionalità dell'applicazione sono le stesse viste in questo articolo, ma con una grafica più gradevole.
La demo, pur essendo più complessa dal punto di vista del codice rispetto a quella mostrata in questo articolo (complessità dovuta dall'implementazione di un'interfaccia grafica più curata), possiede un numero esiguo di classi:
MenuViewController
: offre le stesse funzionalità viste nell'articolo con un'interfaccia grafica minimale.HierarchyViewController
: offre tutte le funzionalità di interazione con i servizi di Dropbox che abbiamo analizzato passo passo fino a questo momento. Rispetto all'applicazione mostrata in questo articolo vengono mostrati a schermo sia i file e le cartelle ottenuti tramite la ricerca o tramite l'acquisizione deiDBMetadata
appartenenti ad un percorso specifico. L'utente, semplicemente facendo tap sulle icone, potrà accedere ai contenuti della cartella oppure visualizzare un file. Effettuando invece un "long press" su un file o una cartella verrà mostrato unaUIActionSheet
(un componente di sistema che presenta all'utente una scelta multipla che possiamo trovare, per esempio, nell'applicazione Mail quando rispondiamo ad un messaggio) che consentirà la cancellazione di tale file o cartella.DocumentViewController
: mostra a schermo un file scaricato dalla cartella condivisa di Dropbox utilizzando la stessa metodologia vista in questo articolo.NewFolderPopooverViewController
: è un view controller che consente la creazione di una nuova cartella.
Il progetto demo presentato all'evento HTML.it Release party 2012 è liberamente scaricabile in allegato.