Come diventare un programmatore iOS Swift

In molti mi chiedono come poter diventare programmatori iOS e quali sono i passi da fare.
Il mio consiglio ad oggi è quello di concentrarsi sul linguaggio di programmazione Swift al fine di averne la piena conoscenza.

Sono convinto che questo linguaggio si espanderà molto fino ad arrivare anche su altre piattaforme in modo produttivo e quindi conoscerlo ed averne la maestria sarà un grande vantaggio.

Ma perché proprio Swift?

Il nuovo linguaggio ideato da Apple sta riscuotendo un grandissimo successo ma alla base c’è una scelta ponderata. Ad oggi Swift è il linguaggio di programmazione di default per i sistemi Apple, non obbligatorio ma con il tempo il supporto ad Objective-C sarà sempre meno frequente.

Questo non significa che non dobbiate conoscere almeno i principi di programmazione Objective-C. Anzi, sarà fondamentale per comprendere alcuni framework e per poter essere in grado di lavorare anche su codice più datato.

Se il vostro obiettivo è quello di lavorare nel mondo delle applicazioni iOS dovrete essere preparati nel caso in cui vi sarà richiesto di mettere le mani su applicazioni già esistenti che nel 90% dei casi saranno scritte in Objective-C.

I primi passi da fare

Questo articolo è scritto per chi ha già una base di conoscenza della programmazione ad Oggetti e dei pattern più usati come MVC (Model-View-Controller).

A questo punto potete addentrarvi nel magico mondo Apple e la prima cosa da fare è studiare la documentazione sull’architettura di iOS.
Una volta appresa potete passare alle linee guida di design di iOS e come ultima parte le linee guida del Review Team che per renderla più interessante e alla portata di tutti, Apple ne ha anche creato una versione a fumetto.

A questo punto potete passare allo studio dei linguaggi di programmazione, Swift e Objective-C.

Libri

Il primo libro da leggere è sicuramente la Guida di Apple su Swift che è possibile anche avere in versione iBook, totalmente gratuito.

Una volta presa dimestichezza con il linguaggio potete iniziare e leggere il libro Advanced Swift: Version 2 di objc.io che personalmente lo reputo una piccola perla insieme a Functional Swift: Updated for Swift 3  sempre di objc.io. Questi due libri hanno un prezzo di $ 39,00 l’uno.

Da tenere presente anche i libri di Ray Wenderlich che spaziano dallo sviluppo di applicazioni fino a quello di videogiochi.

Video

Ogni anno Apple organizza la WWDC (World Wide Developer Conference) che è un evento molto importante per ogni sviluppatore. All’interno di questo evento vengono solitamente presentati i nuovi sistemi operativi e le ultime tecnologie. Durante i 5 giorni vengono fatti degli speech su argomenti ben precisi riguardanti le novità appena introdotte. Potete vedere quindi tutti i loro video sul portale Apple Developer.

Alla Stanford University ogni anno organizzano un corso di sviluppo iOS che poi mettono a disposizione a chiunque, lo trovate a vostra disposizione su iTunes.

Anche l’Università di Pisa ha organizzato un corso di sviluppo iOS, non è aggiornato all’ultima versione ma è comunque molto utile. Potete trovarlo anch’esso su iTunes.

Anche Ray Wenderlich nel suo ottimo archivio ha molti video corsi interessanti e che dovete assolutamente vedere.

Corsi

Ci sono moltissimi corsi di formazione per il mondo iOS sia online che offline. Non ho mai avuto l’opportunità di conoscere le persone che li organizzano quindi non so dirmi qual’è il migliore. Vi faccio un elenco di quelli più famosi.

Un progetto molto interessante e che personalmente seguo è Talks.io sempre dei ragazzi di objc.io dove trattano argomenti specifici in Swift cercando di insegnare e discutere sulle soluzioni migliori da adottare durante lo sviluppo.

Se invece siete di Pisa, Lucca, Livorno o d’intorni, nel corso del 2017 ci saranno novità riguardanti lo sviluppo di applicazioni iOS al Talent Garden di Pisa. Se siete interessati a corsi, workshop e seminari potete iscrivervi alla newsletter e riceverete le novità al riguardo.

Blog

Anche di blog che parlano di sviluppo iOS e Swift ce ne sono tantissimi. Personalmente seguo gli issues di objc.io (anche se ad oggi non stanno portando avanti il progetto) e il blog di Ray Wenderlich.

Da tenere sempre a portata di mano è il blog di Apple su Swift così come il blog We ♥ Swift.

Come ultima risorsa ma non meno importante vi consiglio di tenere a portata di mano il link delle Risorse per gli sviluppatori di Apple dove potete trovare sempre le ultime novità.

Se avete domande o ogni altra necessità potete sempre contattarmi.

Copiare un oggetto con NSCopying

Una richiesta che ci fanno spesso è quella di riuscire a copiare o duplicare degli oggetti.
Capita spesso infatti di avere un oggetto e volerlo duplicare per poi lavorarci sopra così da avere la possibilità di “tornare indietro”.
Ma questa possibilità è utile anche per molti altri scopi.

Objective-C ci da la possibilità di copiare un oggetto, ma per farlo deve essere conforme al protocollo NSCopying.

Come implementarlo

Creiamo il nostro classico oggetto di prova, SampleObject sottoclasse di NSObject.
Seguiamo le istruzioni come nell’articolo “Come salvare un oggetto per poi rileggerlo in seguito“.
Avremo quindi tre proprietà: name, birthDate e age.

Adesso però rendiamolo conforme al protocollo NSCopying aggiungendolo all’interfaccia:

@interface SampleObject : NSObject <NSCopying>

@property (nonatomic, strong) NSString*	name;
@property (nonatomic, strong) NSDate* birthDate;
@property (nonatomic, readwrite) NSInteger age;

@end

Poi nel file di implementazione andiamo ad aggiungere il metodo richiesto dal protocollo -copyWithZone:

- (id)copyWithZone:(NSZone *)zone
{
	SampleObject* object = [[self class] allocWithZone:zone];
	
	object->_name = [self.name copy];
	object->_birthDate = [self.birthDate copy];
	object->_age = self.age;
	
	return object;
}

Ciò che stiamo facendo e creare un oggetto dello stesso tipo in una zona di memoria predefinita per far sì che il sistema gestisca le cose come deve (per farla semplice), poi, tramite l’operatore “->” andiamo a copiare le proprietà dell’oggetto, che saranno le medesime dell’oggetto stesso.
Da notare che anche gli oggetti al suo interno dovranno essere copiati, altrimenti farete puntare le proprietà ad oggetti condivisi.

A questo punto possiamo creare delle copie di SampleObject, vediamo come:

Instanziamo l’oggetto nel nostro view controller:

SampleObject* object = [SampleObject new];
object.name = @"Andrea";
object.birthDate = [NSDate date];
object.age = 28;

Il debugger ci dirà:

Object name Andrea, birthDate 2014-11-01 13:16:46 +0000, age 28

Adesso creiamo una copia:

SampleObject* copiedObject = [object copy];

Se chiediamo al debugger i dati di questo oggetto ci ritroveremo la risposta:

copiedObject name Andrea, birthDate 2014-11-01 13:16:46 +0000, age 28

Ricordatevi che copiare gli oggetti significa duplicare anche lo spazio di memoria occupato, quindi fatelo con prudenza.

Come salvare un oggetto per poi rileggerlo in seguito

Una funzionalità molto utile è quella di poter creare degli oggetti e poterli salvare su disco.

Ad esempio potremmo avere bisogno di salvare una cache o delle impostazioni molto complesse e poterle riutilizzare in seguito.
Objective-C ci aiuta anche in questo con delle classi molto utili e semplici da utilizzare.

Il concetto di base è semplice, dobbiamo trasformare il nostro oggetto in Dati (NSData) e poi salvarlo. Ma se proviamo a farlo ad un nostro oggetto probabilmente riceveremo qualche errore.

La cosa necessaria infatti è che l’oggetto che vogliamo salvare sia conforme al protocollo NSCoding.

Vediamo come utilizzarlo e come poter permettere il salvataggio e il recupero di questi dati.

Creiamo una classe di esempio, sottoclasse di NSObject e con il nome “SampleObject”. Rendiamola conforme al protocollo NSCoding e dichiariamo tre proprietà:

@interface SampleObject : NSObject 

@property (nonatomic, strong) NSString* name;
@property (nonatomic, strong) NSDate* birthDate;
@property (nonatomic, readwrite) NSInteger age;

@end

A questo punto però dobbiamo implementare i due metodi necessari affinché il protocollo permetta di fare il lavoro in modo corretto.

- (id)initWithCoder:(NSCoder *)aDecoder
{
	if ((self = [super init]))
	{
		_name = [aDecoder decodeObjectForKey:@"name"];
		_birthDate = [aDecoder decodeObjectForKey:@"birthDate"];
		_age = [aDecoder decodeIntegerForKey:@"age"];
	}
	return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder
{
	[aCoder encodeObject:_name forKey:@"name"];
	[aCoder encodeObject:_birthDate forKey:@"birthDate"];
	[aCoder encodeInteger:_age forKey:@"age"];
}

Quello che avviene è la codificazione e la decodificazione delle variabili all’interno dell’oggetto quando NSCoding lo richiede.

In particolare c’è metodo di inizializzazione e uno che viene richiamato nel momento in cui cercheremo di codificare l’oggetto.

Adesso torniamo al nostro ViewController ed andiamo ad inizializzare il nostro oggetto. Assegnamo le proprietà:

SampleObject* object = [SampleObject new];
object.name = @"Andrea";
object.birthDate = [NSDate date];
object.age = 28;

A questo punto l’oggetto è completamente funzionante infatti possiamo farci stampare dal debugger i suoi dati:

Object name Andrea, birthDate 2014-10-29 16:43:26 +0000, age 28

Ora proviamo a salvarlo in NSUserDefaults.
Ciò che dobbiamo fare quindi è trasformarlo in NSData e per fare questi ci viene in aiuto la classe NSKeyedArchiver:

NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
NSData* dataObject = [NSKeyedArchiver archivedDataWithRootObject:object];
	
[userDefaults setObject:dataObject forKey:@"object"];
[userDefaults synchronize];

Adesso è quindi salvato all’interno di NSUserDefault dell’utente.
Possiamo ricrearlo quando vogliamo ed anche qui ci facciamo aiutare da un altra classe di Objective-C chiamata NSKeyedUnarchiver:

NSData* restoredDataObject = [userDefaults objectForKey:@"object"];
SampleObject* restoredObject = [NSKeyedUnarchiver unarchiveObjectWithData:restoredDataObject];

Et-Voilà, ecco il nostro oggetto esattamente come prima e il debugger sarà felice di dirci quali sono i suoi dati:

restoredObject name Andrea, birthDate 2014-10-29 16:43:26 +0000, age 28

La procedura è un po’ macchinosa e non rimane facilmente in mente, per non parlare di quando l’oggetto è complesso.. risulterà sicuramente noiosa ma le potenzialità sono infinite.

NSUserDefaults a cosa serve e come funziona

In poche parole potremmo dire che NSUserDefaults è una classe che ci permette di gestire in modo semplice e veloce alcune informazioni impostate dall’utente all’interno della nostra applicazione.

In realtà è molto di più ed una delle più interessanti funzionalità è quella di condividere dati tra applicazioni, ma limitiamoci ad esplorare il funzionamento basilare per la nostra app.

Impostazioni dell’app

L’uso più comune di NSUserDefaults è quello della gestione delle impostazioni dell’applicazione.
Attivare iCloud, disattivare il salvataggio automatico delle foto, impostare il raggio minimo delle regioni da monitorare, salvare lo username dell’utente, sono tutti dati che potremmo impostare all’interno della nostra app.

Ma dove salvare questi preziosi dati? In NSUserDefaults ovviamante!

Ecco quindi come fare. Prepariamo i nostri dati da, un booleano, un intero ed una stringa (dati a caso per l’esempio):

BOOL userWantsBeNotified = YES;
NSInteger regionMinimumRadius = 200;
NSString* username = @"JohnDoe";

Quindi scriviamo:

// 1.
NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];

// 2.	
[userDefaults setBool:userWantsBeNotified forKey:@"userWantsBeNotified"];
[userDefaults setInteger:regionMinimumRadius forKey:@"regionMinimumRadius"];
[userDefaults setObject:username forKey:@"username"];

// 3.
[userDefaults synchronize];

1. Instanziamo l’oggetto userDefaults, come vedete è un Singleton (leggete qui se vi siete persi l’articolo sull’argomento).
2. Settiamo questi dati nell’oggetto userDefaults.
3. Lasciamo salvare i dati al sistema. Questo punto è fondamentale, se lo tralasciate e l’app si chiude, i dati non verranno salvati e quindi saranno persi.

Per leggere questi dati?

Adesso che i dati sono stati scritti vediamo come poterli recuperare:

BOOL fromUD_userWantsBeNotified = [userDefaults boolForKey:@"userWantsBeNotified"];
NSInteger fromUD_regionMinimumRadius = [userDefaults integerForKey:@"regionMinimumRadius"];
NSString* fromUD_username = [userDefaults objectForKey:@"username"];
	
NSLog(@"From UD: userWantsBeNotified %d", fromUD_userWantsBeNotified);
NSLog(@"From UD: regionMinimumRadius %ld", (long)fromUD_regionMinimumRadius);
NSLog(@"From UD: username %@", fromUD_username);

Una riga di codice. Possiamo recuperare questi dati da NSUserDefaults ogni volta che vogliamo.
Se quindi è andato come previsto il debugger scriverà:

From UD: userWantsBeNotified 1
From UD: regionMinimumRadius 200
From UD: username JohnDoe

Curiosità

Se dovete salvare tanti dati prendete in considerazione l’idea di tenere le chiavi da passare a NSUserDefaults in qualche punto comune del codice così da non dover riscriverle ogni volta e minimizzare gli errori.

Link Utili

Come leggere e scrivere un file su disco

Poter scrivere o leggere un file dal disco ci permette di aggiungere funzionalità interessanti alla nostra app.

Ad esempio possiamo implementare una cache per le immagini, salvare i dati creati dall’utente, scaricare file dalla rete e tanto altro.

Ma vediamo come poter creare e salvare dei file.
Partiamo con un semplice file di testo.

Prepariamo il campo di gioco:

// 1.
NSString* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];

// 2.
NSError* error = nil;

1. Prendiamo il percorso completo della cartella dei documenti dell’app.
2. Dichiariamo anche un NSError per controllare gli eventuali errori durante la procedura.

// 3.
NSString* textFilePath = [documentsPath stringByAppendingPathComponent:@"file.txt"];

// 4.
NSString* text = @"Questo è un semplice file di testo";

// 5.
[text writeToFile:textFilePath atomically:YES encoding:NSUTF8StringEncoding error:&error];

NSLog(@"File saved (error: %@)", error);

3. Quindi creiamo il percorso e il filename del nostro nuovo file.
4. Dichiariamo e creiamo una semplice NSString contenente il testo da salvare.
5. Scriviamo quindi il file.

Se tutto va bene dovremmo vedere scritto sul debugger:

File saved (error: (null))

Adesso proviamo a rileggere questo file:

NSString* readText = [[NSString alloc] initWithContentsOfFile:textFilePath encoding:NSUTF8StringEncoding error:&error];
	
NSLog(@"File loaded %@; (error: %@)", readText, error);

La sintassi è davvero semplice, una riga di codice. Il debugger dirà:

File loaded Questo è un semplice file di testo; (error: (null))

Semplice no? Andiamo adesso a complicare leggermente le cose.

E se fosse un’immagine?

Assumiamo di avere un’immagine nella nostra app, dentro il Bundle dell’app.
(Il Bundle è il pacchetto dell’app stessa, dove vengono inseriti tutti i file contenuti ed aggiunti al progetto)

// 1.
NSString* imagePath = [[NSBundle mainBundle] pathForResource:@"sample_image" ofType:@"png"];

// 2.
NSData* imageData = [NSData dataWithContentsOfFile:imagePath];

// 3.
UIImage* image = [UIImage imageWithData:imageData];
	
NSLog(@"Image Loaded %@", image);

1. Recuperiamo il percorso del nostro file. In questo caso è “sample_image.png” che è stato aggiunto al progetto.
2. Leggiamo il file dal disco sottoforma di NSData (dati puri).
3. Creiamo un oggetto UIImage da questi dati.

A questo punto avremo la nostra immagine e il debugger ci dirà:

Image Loaded

Proviamo a salvarla sul disco, nella cartella dei documenti dell’app.

// 1.
NSData* saveImageData = UIImageJPEGRepresentation(image, 0.8);

// 2.
NSString* imageFilePath = [documentsPath stringByAppendingPathComponent:@"image.jpg"];

// 3.	
BOOL saved = [saveImageData writeToFile:imageFilePath atomically:YES];
	
NSLog(@"Image saved %d", saved);

1. Creiamo un NSData dalla nostra immagine. In questo caso sarà la rappresentazione JPEG con qualità all’80%.
2. Dichiariamo e creiamo il path e il nome dell’immagine.
3. Scriviamo i dati nel disco.

Se tutto è andato come previsto il debugger scriverà:

Image saved 1

Ecco quindi che abbiamo letto e scritto un file di testo e un’immagine.

Pensate a tutte le possibilità adesso!