Le app iOS vengono realizzate attraverso un modello di programmazione esteso: il model-view-controller pattern (o MVC). Il MVC è un pattern di alto livello usato per descrivere l’architettura globale di un’applicazione e per classificare gli oggetti secondo i ruoli da essi ricoperti. Nel MVC gli oggetti sono più riutilizzabili e i programmi più facili da modificare.

In questo pattern gli oggetti sono suddivisi in tre categorie. I model objects incapsulano i dati e la logica dell’app per modificarli, e non hanno collegamenti espliciti con l’interfaccia utente né conoscenza di essa.

L’interfaccia utente è rappresentata dai view objects. Il ruolo di questi oggetti è quello di rappresentare le informazioni all’utente. Sanno come mostrare i dati e permettono all’utente di interagirvi, tuttavia non si occupano di storage.

Ciò che unisce questi due layers sono i controller objects, i quali assicurano che le views abbiano accesso ai model objects da mostrare e diffondono i cambiamenti in entrambe le direzioni. A causa del loro ruolo, tendono ad essere tra gli oggetti meno riutilizzabili nell’architettura delle app.

iOS utilizza una particolare tipologia di controller objects, chiamati View Controllers. Tali controller prendono parte al pattern MVC mediando tra i model objects e i view objects; in più vanno ad aggiungersi altre responsabilità. Una di queste è che partecipano alla responder chain, dunque ricevono eventi (come – ma non solo – i touch events). I view controllers ricevono inoltre notifiche sullo stato della memoria da OS e sono responsabili della sua gestione.

Il problema dei View Controller smisurati

Dal momento in cui i view controller hanno responsabilità aggiuntive in iOS, sono costretti a gestire un grosso carico di lavoro. Per questo motivo, non è difficile che altro codice non appartenente ai view controller si insinui al loro interno. Alla fine si trasformano in enormi monoliti contenenti qualsiasi tipo di funzionalità, perché aggiungere un ulteriore metodo è semplicissimo… finché non ci si ritrova con un oggetto sproporzionato difficile da modificare e da ristrutturare, rendendo di fatto i view controller ancor meno riutilizzabili di quanto non lo siano già. Ecco perché vengono simpaticamente definiti “massive” dai developers.

In passato mi sono reso colpevole di questa pratica e, per di più, sono ancora incline a ripeterla nonostante sia qui a consigliarvi di evitarlo. È qualcosa di così radicato nelle procedure di realizzazione di app iOS che anche i più esperti sono costretti a rimanere vigili per non cadere nell’errore. Personalmente uso le righe di codice come unità di misura per valutare quando un view controller sta diventando troppo grande.

Se un view controller supera le 500 righe, allora vuol dire che al suo interno vi saranno molto probabilmente funzionalità che non dovrebbero esserci. Controlliamo con attenzione.

Cercate di non gonfiare troppo un view controller prima del refactoring. Certo, è sempre possibile rimuovere delle funzionalità in un secondo momento, ma quando si continua a posticipare il codice finirà per aggrovigliarsi ad altro codice e diverrà tutto molto più complesso. Inoltre, vi sono spesso barriere psicologiche che bloccano il developer dai refactoring di grande portata, per non parlare della paura di andare a toccare codice perfettamente funzionante.

Come scrivere View Controllers più piccoli

Ci sono alcune linee-guida che seguo personalmente per assicurare le proporzioni giuste dei miei view controllers. Queste regole derivano dal ruolo che un view controller ricopre nel pattern MVC – ossia permettere la transizione di informazioni tra il model e le views. I view controller, visto il ruolo nella gestione della memoria, possono anche eseguire il set-up e gestire i cicli di vita degli altri oggetti.

La logica di un’app non è riportata tra questi compiti. È qui principalmente che il codice superfluo fa la sua entrata. I view controller spesso prendono parte alla business logic di un’app influenzando gli oggetti, integrando algoritmi, interagendo con il network o con il disco. Di fatto assorbono gran parte del codice che dovrebbe andare nel model layer. Si tratta dunque di parti da inserire in classi specializzate e separate, mentre il view controller dovrebbe limitarsi ai cambiamenti dovuti agli input dell’utente.

Un caso specifico è rappresentato da delegates e data sources. In iOS, le view complesse – come le table views o le collection views – vengono configurate da oggetti esterni chiamati delegate e data source. Molto spesso questa funzionalità è implementata all’interno dei view controllers e ciò lo ritroviamo in molti tutorial e perfino nella documentazione Apple. C’è perfino una specifica sottoclasse di UIViewController, la classe UITableViewController, che si imposta automaticamente come delegate e data source della sua table view.

L’approccio migliore prevede la separazione di questi comportamenti in classi differenti. I data source sono un ottimo esempio del caso. Se, ad esempio, i data per una table view sono forniti attraverso un array – come spesso accade – potrete scrivere i data source objects che controllano l’array e offrono tutti i comportamenti specificati dal protocollo UITableViewDataSource: restituendo il conto dell’array come il numero di righe nella table view, riportando l’oggetto nell’index appropriato quando la table view lo richiede, inserendo ed eliminando oggetti nell’array e così via.

Ciò rimuove una buona porzione di codice dal view controller e ha inoltre il beneficio di essere più riutilizzabile: la prossima volta che avrete un table view con un array di oggetti, basterà creare una nuova istanza del vostro array data source, assegnarlo alla table view e il gioco sarà fatto. Questa classe data source si può inoltre trasferire ad altre app. Potrete avere un data source per Core Data oppure un data source per gli oggetti portati dal network. Una volta scritti e testati, saranno prontamente disponibili per tutte le vostre app future. In più, i vostri view controllers diventeranno più generici e potranno dunque essere riutilizzati con diversi tipi di data source nell’intera app.

Anche il lato UI offre a codice estraneo una via di infiltrazione all’interno di un view controller. Molte volte i view controllers si legano attraverso outlets a ogni singola view sullo schermo, per poi procedere con le impostazioni inerenti ad aspetto e posizione. In realtà si tratta di codice legato alla presentazione, quindi è meglio isolarlo, ad esempio, nelle sottoclassi di UIView. Ancora un volta, i view controllers risulteranno più snelli, generici e riutilizzabili in altre parti dell’app o in altre app. Del resto, è proprio ciò che fa una table view: integrare specifico codice UI così da avere un componente pronto all’uso da applicare in diverse parti.

Parlando di table views, bisogna fare attenzione anche alle celle, essendo queste un altro punto in cui il codice UI riesce a penetrare. Il colpevole in questo caso è il metodo -tableView:cellForRowAtIndexPath:. In questo metodo, i model objects vengono portati dal data source e usati per impostare il contenuto delle table view cells attraverso outlets o, ancora peggio, accedendo direttamente alle subviews della cella usando il metodo della classe UIView viewWithTag:

Sfortunatamente questo codice è presente anche nella documentazione Apple, quindi a seguirlo sono in tanti (anche io in passato, non conoscendo ancora di meglio). Ciò che dovrebbe farsi qui è creare una sottoclasse di UITableViewCell che si occupa di popolare tutti i campi della cella. In questo modo il -tableView:cellForRowAtIndexPath: dovrà solo riportare un’informazione dal data source e passarlo alla cella – operazione che richiede appena due righe di codice.

A proposito, come avrete notato, la cella stessa diventa una classe più generica e riutilizzabile. Una volta creata la custom class per le table view cells, altre logiche potranno aderire in modo naturale, come ad esempio il calcolo dell’altezza della cella quando questa è dinamica.

***

Autore: Matteo Manferdini
Articolo originale: How to Keep Your View Controllers Small for a Better Code Base

NO COMMENTS

LEAVE A REPLY