Generics con il linguaggio Swift | Dai flessibilità al tuo codice

Giuseppe Sapienza

Compatibile con Xcode 8

Hai un problema con il tutorial? scrivi un commento in fondo alla pagina - non mordo!

Peppe, sei un tipo alquanto generico. Quello che può sembrare un insulto, per gli sviluppatori, è quasi un complimento. Eh si, perché la programmazione generica o, Generics con il linguaggio Swift, è qualcosa che eleva le tue capacità di scrivere e progettare le applicazioni.

Se è la prima volta che ne senti parlare, non fartene una colpa. Chi sa cosa sono i Generics, o crede di saperlo, si muove sempre nell’oscurità. Aspettano il momento buono per uscirsene allo scoperto con questi fantomatici Generics per farti sentire in imbarazzo o quanto meno perplesso.

Ma bando alle ciance, cosa sono i Generics e come si usano con il linguaggio Swift?

Partiamo da un presupposto. Se vuoi diventare un ottimo sviluppatore, non solo di applicazioni iOS, devi essere in grado di riutilizzare il codice che crei. Non solo tra un progetto ed un altro ma anche tra i file del tuo Xcode Project. Quindi, presta attenzione a questo strumento, prima o poi ti ci imbatterai.

Ma andiamo a noi. Per farti capire cosa sono i Generics partiamo da un esempio classico.

Ci sono due variabili all’interno della tua applicazione, b, e ad un certo punto ti serve invertire i loro valori. Cioè deve contenere il valore di il valore di a. In pratica, se a=6 e b=1, alla fine della storia b=6 ed a=4.

Che difficoltà c’è in questo? Nessuna aggiungerei io. Dato che conosci le funzioni del linguaggio Swift, provvederesti subito a creare una funzione di swap analoga alla seguente:

E se adesso ti chiedessi di provare ad invertire due variabili String?

Aspetta un momento Peppe, ma il codice non è sempre lo stesso con i Tipi di Dato della funzione cambiati? 

Esattamente. Solo che, non potendo più utilizzare la funzione swapInt, creata poc’anzi, saresti costretto a crearne una nuova, con il corpo (cioè il codice) uguale e l’intestazione diversa:

Qui entrano in gioco i Generics.

I Generics permettono di creare funzioni, classi, strutture, enum e quant’altro in con una sintassi universale. Cioè che non dipendono da un Tipo di Dato definito (che sia di sistema o generato dallo sviluppatore).

Comincia a capire l’importanza di questo strumento?

Allora vediamo insieme come usare i Generics con il linguaggio Swift. Si parte!

Capire i Generics

Essendo per definizioni generici, i Generics non hanno una simbologia definita. Cioè non esiste l’equivalente di String/Double ecc per i Generics. Lo dico perché ci cascano tutti.

Esistono, invece, delle regole e delle sintassi particolari che ti permettono di definire un qualcosa come Generics (altri linguaggi di programmazione, tipo Java, esortano ad utilizzare una simbologia ben definitiva anche se non obbligatoria).

La regola è abbastanza semplice.

Dopo il nome della funzione, classe ecc vengono fatte seguire le parentesi angolari <>. Da questo momento in poi, il compilatore, sa che stai per utilizzare i Generics.

Cosa va dentro le parenti angolari?

Dentro le angular brackets <> puoi scrivere qualsiasi cosa. Questo qualcosa che scriverai, farà da placeholder o rimpiazzo, per il Tipo di Dato che andrai ad utilizzare realmente.

Dato che è difficile da spiegare a parole, ti faccio subito un esempio.

Riprendiamo la funzione di swap e proviamo a convertirla in una Generics function. 

Chiamiamo swapTwoElement e subito il nome piazziamo le parentesi angolari. All’interno delle parentesi mettiamo la lettera maiuscola T (diminutivo di Type). A questo punto, invece di utilizzare un tipo reale, per i parametri della funzione, potrai utilizzare quella T messa all’interno delle parentesi angolari.

Cioè quel T, messo dentro le parentesi angolari, viene identificato solo in questo contesto, come nuovo Tipo di Dato fittizio da poter utilizzare a nostro piacimento.

E l’utilizzo? La cosa fantastica e che, all’occhio dell’utilizzatore, non cambia assolutamente niente se non il dover utilizzare una ed una sola funzione per tutte queste operazioni di swap:

Voglio essere logorroico.

Non ho usato T perché me lo ha imposto Steve Jobs dall’alto dei cieli. Al posto di T, dentro le angolari, avrei potevo scrivere qualsiasi cosa. L’importante è capire che è solamente un nome fittizio (chiamato placeholder) per permetterti di lavorare con qualcosa di astratto e non definito.

Cioè potevi scrivere anche:

Questa è la regola principale per capire e comprendere i Generics del linguaggio Swift. Adesso vediamo come applicarli e alcuni casi particolari.

Classi Generics

Anche le classi possono essere definite come tipi Generics.

Immagina di voler creare una classe che gestisce una lista di cose non definite. Possono essere Oggetti, String, Double, non ha importanza. La classe deve poter restituire il primo elemento e l’ultimo elemento della lista che gestisce.

Anche per le classi, dopo il nome, vengono fatte seguire le parentesi angolari contenenti il nome del nuovo tipo generico. Le proprietà ed i metodi potranno utilizzare quel placeholder come nuovo tipo fittizio:

Per utilizzarla, in fase di inizializzazione del nuovo oggetto, subito dopo il nome dovrai specificare il Tipo di Dato reale che dovrà utilizzare la Lista, i suoi metodi ed attributi.

Per esempio, se volessi creare una Lista di stringhe, scriverei:

Potresti anche utilizzarlo come contenitore di altri oggetti.

Se la tua app serve a gestire una fattoria di animali, ti potrebbe essere utile la classe Lista generica per conservarli tutti dentro ad un contenitore:

Nota bene che non ho dovuto riscrivere una nuova classe per la gestione della fattoria. Ho semplicemente riutilizzato la classe Lista con un contesto differente.

Gli stessi esempi, senza Generics, avrebbero comportato la scrittura di due classi Lista differenti.

Type Constraint

Esercizio 0. Proviamo a creare una semplice funzione Generics che permetta di confrontare due valori dello stesso tipo. Se i valori sono uguali deve restituire true.

Il nostro Playground genera un errore inaspettato. Ovvero ci comunica che l’operatore di comparazione == non può essere applicato a due oggetti di tipo T, perché?

Generics e Contraints Type

Questo accade perché, i Generics, per loro natura sono svincolati da qualsiasi tipo di vincolo.

Per dirla in maniera più tecnica, i Generics, non ereditano da nessun Protocol o Classe.

Peppe, cosa vuol dire?

I Tipi di Dato del linguaggio Swift, come le String, Double ecc sono delle Struct che ereditano alcuni comportamenti da diversi protocolli. I protocolli, ti ricordo, obbligano una Classe/Struct ad implementare dei metodi.

Nel particolare, i Tipi di default, implementano un protocollo chiamato Equatable. Il protocollo Equatable permette di determinare il comportamento dell’operatore == per quel particolare Tipo.

In base a questa semplice considerazione possiamo dire con certezza che il nostro tipo generico T non eredita dal protocollo Equatable.

Voglio arrivare a dirti che, i Generics, possono essere specializzati facendogli ereditare delle caratteristiche dalle Classi o Protocolli. Quest’operazione prende il nome di Type Constraint (costringere un tipo a comportarsi in un determinato modo).

Puoi aggiungere un Constraint ad un tipo Generics utilizzando questa sintassi:

Se adesso costringiamo la funzione confronta ad utilizzare il protocollo Equatable, riusciamo facilmente a risolvere l’errore:

Esercizio 1. Vogliamo creare una funzione generica che permetta di contare, all’interno di un Array qualsiasi, la ricorrenza di un determinato valore. Per esempio, se hai il seguente array [4,67,4,7,2] ed il valore da cercare è 4, la funzione deve stampare “ci sono due 4 nell’array“.
Ovviamente la funzione deve essere riutilizzabile in altri contesti, quindi anche con array di String, Oggetti ecc.

Se sei alle prime armi con il linguaggio Swift ti puoi fermare anche qui con i Generics. Il prossimo paragrafo l’ho pensato per chi ha già un po’ le mani in pasta con lo sviluppo delle applicazioni.

Protocolli e Associated Type

La regola vale anche per i protocolli. Anch’essi possono essere definiti come Generics. Ma cosa succederebbe se i metodi del protocollo avessero bisogno di gestire oggetti che implementano quel particolare protocollo?

Per queste particolare situazioni, si può creare un protocollo di tipo Generics, utilizzando la parola chiave associatedtype seguita dal nome di un Generic Type per riferirti a qualsiasi tipologia d’oggetto non ancora specificato.

Vediamo un esempio.

Immaginiamo d’avere un protocollo che definisca i metodi di login ad una piattaforma (Firebase, il tuo Oauth sys ecc).

Il protocollo lo chiamiamo Loggable. Al suo interno, definiremo una funzione login con un completionHandler (una closure) che conterrà l’istanza del LoggableObject (il GenericType che conterrà i valori de login corretto) ed un ErrorType per la gestione dell’errore (un altro generic enum definito sotto):

Adesso crea una classe Utente che implementa questo protocollo. Nella fase di implementazione dei metodi definiti dal protocol Loggable potrai aggiungere, al primo parametro della withCompletion, il tipo della classe in cui si trova il metodo:

Interessante, no?

Esempio protocol Loggable applicato
Esempio di un protocol Generics per la gestione di una lista applicando ad una classe generica per la descrizione della lista

Con questo mi fermo qui come primo approccio ai Generics con il linguaggio Swift. Voglio comunque elencarti alcune motivazioni che dovrebbero spingerti ad utilizzarli.

Considerazioni

Se il tuo obiettivo è quello di scrivere un paio di applicazioni, allora no, i Generics non fanno per te. I Generics nascono per la riusabilità e la condivisione del codice. La maggior parte dei framework di terze parti, come quelli standard, utilizzano i Generics proprio per lasciarti piena autonomia d’utilizzo.

Quindi se stavi cercando un sistema per aumentare la tua capacità di scrivere, riutilizzare, astrarre e condividere il codice, allora i Generics fanno proprio al caso tuo.

Non ho trattato tutti gli argomenti che concernono il mondo dei Generics. Essendo dei concetti astratti avrai bisogno di una certa quantità di tempo per poterli sentire pienamente tuoi. Ecco perché non ho voluto sovraccaricare questo tutorial.

Sicuramente scriverò un altro articolo in cui ti farò vedere qualche utilizzo applicato dei Generics con il linguaggio Swift.

Se è la prima volta che approdi sul mio sito, dai un’occhiata a:

Buona Programmazione!

About
Classe 1993. Sono uno studente di ingegneria informatica a Catania, un istruttore di arti marziali ed un libero sognatore. Mi sono avvicinato al mondo della programmazione all'età di 15 anni per motivi che è meglio far rimanere segreti. Oggi passo il mio tempo a studiare e realizzare applicazioni per i dispositivi Apple, diffondendo ciò che imparo sul mio portale xCoding.it. Quando non scrivo sul blog lo faccio per la rivista bimestrale ioProgrammo.
  • Dario Carnesecchi

    Argomento interessante! Per ora mi sono fermato dove consigliavi di farlo visto che sono ancora alle prime armi con swift, ma avrri una domanda. Quindi, se ho ben capito, questi generics sono qualcosa di simile agli id type per il linguaggio obj-c giusto?

Start typing and press Enter to search