Inauguriamo qui la prima serie di articoli sul linguaggio di programmazione Swift che, da circa un anno, ho cominciato a studiare in maniera totalmente autonoma nel tempo libero (poco, troppo poco!). Le risorse online per l’apprendimento sono praticamente infinite ma se vogliamo valutarne la qualità la scelta si riduce a 4-5 scelte.
Ci tengo a precisare che questi primi articoli non vogliono essere dei tutorial (non ho né l’esperienza né la competenza per farlo al momento) ma piuttosto un riassunto di quanto appreso nella giornata di studio e dei progressi svolti.
Il più grande ostacolo da me incontrato in questi primi mesi di studio dell’arte della programmazione è stato il costante avere la sensazione di non sapere riprodurre in autonomia quanto appreso. Alcuni tutorial preferiscono un approccio puramente pratico indicando riga per riga cosa scrivere e spiegando in maniera marginale il perché, mentre altri —pochi— si curano di spiegare al malcapitato studente ogni singolo aspetto dell’argomento affrontato. Venendo da un background totalmente diverso dalla programmazione —la musica classica— e non avendo toccato una formula matematica e/o un problema di logica dalla V liceo (ben 13 anni fa) risulta per me essenziale capire cosa diamine stia succedendo.
Nelle giornate del Black Friday dello scorso novembre ho fatto la pazzia ed ho acquistato l’abbonamento al sito di Ray Wenderlich, che sicuramente gli esperti del settore conosceranno. Questo mi ha dato accesso a tutti i video-tutorial del sito, circa 1500, sicuramente più di quanto riuscirò a fare. La cosa più interessante è la possibilità di avere un percorso “personalizzato” in base alle proprie preferenze.
Per esempio, io ho scelto il percorso “iOS e Swift” e, in questo momento, sto affrontando la terza parte del corso, ovvero la costruzione della seconda applicazione (per chi non conoscesse la struttura del corso, basata liberamente sui libri iOS Apprentice e Swift Apprentice, comincia con la costruzione di un gioco—Bull’s Eye— e con una parte di studio approfondito del linguaggio Swift—personalmente affrontato in maniera troppo veloce per potere essere seguita da qualcuno senza almeno una base in programmazione).
Questa seconda applicazione si chiama Checklist e non è nient’altro che un clone molto basilare dell’applicazione Promemoria che tutti noi possiamo trovare sul nostro iPhone, iPad o Mac. Credo che in italiano questo tipo di applicazione si chiami “elenco di controllo”.
Oggi ho affrontato la seconda sezione di questa terza parte, dedicata alla spiegazione del concetto di MVC (Model-View-Controller). Teoricamente il concetto sembra semplice ma la sua applicazione è tutt’altro che immediata, viste le infinite variabili al suo interno. In pratica abbiamo un modello (Model) che altro non è se non una scatola contenente i dati con i quali vogliamo lavorare, nel nostro caso conterrà i promemoria che verranno in seguito visualizzati sullo schermo (View), quest’ultimo programmato da un centro di controllo (Controller). Questo è quanto ho capito io della storia e potrebbe essere lontanissimo dalla verità quindi se qualche esperto legge quest’articolo e vuole commentare è benvenuto!
La sostanza del discorso sembra essere il fatto che iOS come sistema funziona a messaggi tra i diversi oggetti dell’applicazione: il centro di controllo riceve un messaggio dal modello e lo invia allo schermo. Se poi l’utente preme un bottone sullo schermo e cancella uno dei promemoria o lo segna come completato, lo schermo manda un messaggio al centro di controllo che provvederà ad aggiornare il modello. L’idea dietro questo caos organizzato è fare sì che ognuno dei tre elementi si debba solo occupare di una mansione ben precisa, senza necessariamente sapere di cosa l’altro elemento si stia crucciando! Ovviamente sto spiegando il tutto nella mia visione romantica del mondo quindi cerchiamo di andare più a fondo.
La prima sezione del corso la salto a piè pari perché tratta della costruzione di base dell’interfaccia e dei primi passi nell’utilizzo della classe UITableViewController. Se interesserà tornerò indietro però in questo momento mi serve imprimere nella mente e nella conoscenza quanto appreso oggi.
Situazione di partenza
Partiamo quindi da questa situazione (in modo da poter seguire):
Nella Document outline a sinistra abbiamo semplicemente il Main.storyboard che vediamo rappresentato nell’immagine, l’AppDelegate.swift—presente in ogni applicazione—, e il nostro ViewController che abbiamo rinominato ChecklistViewController. Ciò che è stato inserito precedentemente non è importante perché verrà presto cancellato e sostituito da qualcosa di molto più ottimizzato.
È estremamente difficile per una persona agli inizi come lo sono io di seguire la velocità a cui questi insegnanti procedono, balzando da una parte all’altra del codice a cancellare e riscrivere tutto di continuo, però a quanto pare si fa così e non c’è altro modo di imparare. In questa serie di articoli io mostrerò il codice nella forma in cui si trova alla fine della sessione di apprendimento, risparmiandovi dunque tutta la sofferenza ed il sudore versato dal sottoscritto, cercando comunque di spiegare il perché ogni riga di codice si trovi dove si trova.
Creazione del modello
Per poter avere dei promemoria nella nostra applicazione dobbiamo avere un oggetto che lo rappresenti e un contenitore che contenga lui ed i suoi simili.
Come prima cosa creiamo un nuovo file Swift (scorciatoia ⌘+N) e chiamiamolo ChecklistItem.swift. In esso creiamo una classe con due proprietà ed un metodo. Le due proprietà, text e checked, rappresenteranno il testo del nostro promemoria e il se sarà marcato come completato oppure no. Il metodo invece ci servirà per cambiare lo stato della proprietà checked da vera a falsa e viceversa. Il perché questo metodo debba essere qui lo vedremo più avanti.
In sostanza il codice di questa classe è relativamente semplice:
class ChecklistItem { var text = "" var checked = false func toggleChecked() { checked = !checked } }
Questo rappresenta il nostro promemoria ma per permettere in futuro all’utente di aggiungere degli elementi personali abbiamo bisogno di un array, ovvero un insieme ordinato di elementi.
Questo contenitore sarà la nostra lista di promemoria e conterrà una proprietà al suo interno che altro non sarà che un array di istanze di ChecklistItem. Creiamo un altro file Swift e chiamiamolo, ad esempio, ToDoList.swift. Al suo interno inseriamo una proprietà variabile todos di tipo [ChecklistItem] e la inizializziamo come array vuoto con la sintassi = [ ].
Il tutorial aggiunge un inizializzatore (!!!) per far sì che lo schermo abbia dei dati ma, personalmente, questa parte può essere opzionale alla fine perché serve solo a capire se l’app sta funzionando oppure no. In questo caso, all’interno di un metodo init() inseriamo cinque proprietà costanti di tipo ChecklistItem e, con la dot notation, andiamo a creare per ognuno di essi un testo ed andiamo ad aggiungerlo al nostro array appena creato.
In sostanza, per n elementi, scriveremo ogni volta:
let row(n-1)Item = ChecklistItem() row(n-1)Item.text = "Insert your todo text here" todos.append(row(n-1)Item)
n-1 è perché gli array contano partendo da zero quindi il primo elemento sarà l’elemento 0, il secondo l’elemento 1 e così via. Per un violoncellista come me è estremamente buffo non averci mai pensato prima: la prima nota di una corda si suona con il dito 0, ovvero la corda vuota!, la seconda con il dito 1 e via dicendo—per quanto le similitudini scompaiano molto presto!
Gestione del Centro di Controllo
Il nostro centro di controllo (Controller) è il file ChecklistViewController.swift rappresentato dalla classe omonima che si conforma al protocollo UITableViewController. Questo protocollo ci permette di usufruire di una serie di funzionalità che ci serviranno per fare funzionare la nostra TableView.
Sebbene non contemplato dal tutorial quando io creo un’app amo tenere le cose in ordine ed ho quindi creato alcune sezioni. In Xcode, l’IDE di base necessario per creare app per iOS, scrivendo
// MARK: - Table View Data Source
andrò a creare una sezione divisa nella barra di navigazione in alto al centro, cosa che mi permetterà di ritrovare la strada con relativa facilità.
Subito sotto la dichiarazione della classe creiamo una proprietà per contenere la nostra lista di promemoria. La chiamiamo todoList e sarà di tipo TodoList. Non la inizializziamo—anche se non ho capito il perché, forse mi devo rivedere la sessione sulle classi— bensì creiamo un “inizializzatore obbligatorio” nella seguente forma:
required init?(coder aDecoder: NSCoder) { todoList = TodoList() super.init(coder: aDecoder) }
La maggior parte di questi gentili signori che hanno fatto della pedagogia della programmazione la loro vita dicono spesso—e lo dicono tutti eh?, senza eccezioni—:
Non ti preoccupare se trovi difficili o se non capisci! Ripetendo ti diventerà più chiaro ed i concetti affonderanno!
Sinceramente questo mi ricorda un po’ alcuni metodi didattici musicali in cui ti fanno ripetere migliaia di volte le cose per imitazione e senza comprendere un fico secco.
Sia nella musica che in ogni altra disciplina sono in completo disaccordo con questo approccio. Mi va benissimo l’allenamento dei neuroni specchio però mi si devono spiegare le cose e poi darmi un discreto numero di esercizi applicativi per fare affondare la questione.
In un corso di Computer Science addirittura, quando ho chiesto come si pensava di poter realizzare la sfida della settimana, mi si diceva che il corso prevedeva che lo studente andasse su Google a cercare la soluzione ed imparasse da lì qualora non riuscisse da solo—impossibile con le risorse fornite—, alludendo a come il metodo europeo di pedagogia fosse totalmente diverso e quindi sbagliato. Ma basta digressione…
La funzione viewDidLoad() per adesso giace lì ferma e silente e ci guardiamo bene dal toccarla. Occupiamoci per prima cosa della sezione Table View Delegate Methods ovvero dei metodi che faranno funzionare la nostra tabella.
Il primo metodo è chiamato didSelectRowAt e descrive il comportamento della cella nel momento in cui l’utente va a cliccare su di essa. Tramite l’optional binding ci assicuriamo che la cella esista, ricordandoci che qualora non facessimo ciò e il sistema non riuscisse a trovare una cella l’app crasherebbe. Al suo interno incapsuliamo l’oggetto corrispondente al nostro promemoria in una proprietà costante, invochiamo il metodo per decidere se la cella debba avere o meno il segno di completamento (configureCheckmark, che creeremo tra poco) e animeremo la deselezione della cella stessa.
if let cell = tableView.cellForRow(at: indexPath) { let item = todoList.todos[indexPath.row] configureCheckmark(for: cell, with: item) tableView.deselectRow(at: indexPath, animated: true) }
A questo punto creiamo i due metodi per gestire il contenuto della nostra cella.
Per il testo vogliamo essere sicuri che la cella abbia un elemento (view) con un tag (etichetta, identificatore…) uguale a quello da noi usato nella storyboard, lo vogliamo “castare” ovvero trasformare in uno UILabel e, a quel punto, immagazzinare nella proprietà text del nostro label il testo dell’oggetto da noi creato in precedenza. È tutto molto incastrato quindi è facilissimo perdersi a questo punto perché non si capisce più a quale item ci si riferisca e perché… Ovviamente, se noi chiamiamo la funzione configureText(for: with:) nella funzione cellForRowAt (come effettivamente faremo), essa dovrà trovare un oggetto chiamato item già presente lì, altrimenti non funzionerà proprio nulla e l’app non partirà neanche. Possiamo quindi dire che l’item principale è quello che creeremo nella sezione successiva e che questo sia solo un momentaneo segnaposto.
func configureText(for cell: UITableViewCell, with item: ChecklistItem) { if let label = cell.viewWithTag(1000) as? UILabel { label.text = item.text } }
La configurazione del segno di completamento è più lunga ma più semplice a livello di concetto perché richiede soltanto un if-else. In pratica: se l’oggetto è già marcato come completato noi andremo a segnare il tipo accessorio della cella su nessuno, andando effettivamente a rimuovere il segno mentre se l’oggetto non sarà ancora stato marcato noi andremo a metterci il segno.
func configureCheckmark(for cell: UITableViewCell, with item: ChecklistItem) { if item.checked { cell.accessoryType = .none } else { cell.accessoryType = .checkmark } }
Se noi ci fermiamo qui l’app funzionerà, lancerà, ma il segno di completamento non reagirà ai nostri comandi. Sinceramente dal tutorial non ho capito perché ci sia questo bug. In pratica noi chiamiamo questo metodo dal didSelectRowAt e lui, trovando la proprietà del ChecklistItem di base false farà partire l’else e quindi dovrebbe mettere il checkmark. Ma invece non lo fa…
Per fare sì che funzioni dovremo aggiungere alla fine del metodo la linea:
item.toggleChecked()
che cambia la proprietà dell’oggetto da false a vera. Il problema è che il segno di completamento non è direttamente collegato alla proprietà… o almeno io non capisco perché questa linea messa alla fine faccia funzionare la baracca mentre senza di essa non funzioni.
Ad ogni modo, avendo adesso tutti gli ingredienti a nostra disposizione, possiamo popolare la nostra tabella nella sezione Table View Data Source ovvero “sorgente dei dati per la tabella”. In questo caso, essendo l’applicazione ancora molto piccola, ci saranno solo due metodi.
Il primo si chiama numberOfRowsInSection e come il nome implica ci chiede di indicare quante file occorre creare per ogni sezione della nostra tabella. In questo caso la tabella ha una sezione soltanto e noi vorremo che iOS crei tante file quanti sono i nostri promemoria all’interno della nostra todoList. Scriveremo allora
return todoList.todos.count
Il secondo metodo si chiama didSelectRowAt: che, come implica il nome, ci permette di programmare il comportamento della tabella quando l’utente “clicca” selezionando una fila (row). Innanzitutto bisogna comprendere che nel primo metodo creato poco sopra noi abbiamo solo stabilito QUANTE file creare, non COSA metterci dentro.
La TableView in iOS funziona in modo estremamente intelligente ed ecologico perché una volta che avrò finito le celle visibili sulla schermata (per esempio scrollando verso l’alto) il sistema riciclerà la prima cella scomparsa e la utilizzerà per farne apparire una in basso, come per magia. Per approfittare di questa funzione dobbiamo creare una cella:
let cell = tableView.dequeueReusableCell(withIdentifier: "ChecklistItem", for: indexPath)
L’identificatore è quello che abbiamo dato nella storyboard mentre indexPath è l’indice a cui si trova la cella partendo dalla più alta (zero!).
Creiamo in seguito un oggetto che rappresenterà (o meglio, conterrà) il nostro promemoria per quella cella:
let item = todoList.todos[indexPath.row]
A questo punto chiamiamo due metodi configurati in precedenza e ritorniamo la cella appena creata. Nel primo metodo configuriamo il testo della cella e nel secondo il suo segno di completamento.
configureText(for: cell, with: item) configureCheckmark(for: cell, with: item)
E voilà!
Questa sezione è terminata ed ho scritto anche troppo però volevo cercare di spiegare a me stesso ciò che avevo appena fatto. Probabilmente domattina non mi ricorderò un tubo e dovrò comunque dedicarmi alla parte del sassofono per il brano di Blank che sto scrivendo. Forse domani sera o, più probabilmente, sabato / domenica riuscirò ad andare avanti su questo progetto e forse scrivere un nuovo articolo.
Nel frattempo è uscita la versione 2019.1 di Sibelius e voglio recensire le novità.
Buonanotte mondo!
> Shut Down…