“Protocol Oriented Programming” per la gestione degli storyboard segue

Uno dei problemi ricorrenti nello sviluppo delle applicazioni iOS è la gestione della Storyboard e dei segue. Questi vengono definiti all’interno dello storyboard stesso, mentre alcune operazioni devono essere poi effettuate via codice. Tipicamente via codice si effettuano passaggi di dati o configurazioni che devono essere fatte a runtime.

Situazione attuale

Chiunque abbia utilizzato uno storyboard e i suoi segue avrà poi scritto delle istruzioni simili:

@IBAction func doSomething() {
    performSegueWithIdentifier("detailSegue", sender: nil)
}

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if segue.identifier == "detailSegue" {
        ...
    }
}

Giusto come ripasso, il primo metodo permette di invocare un segue che è stato definito nello storyboard (in questo caso ‘detailSegue’), mentre il metodo prepareForSegue:sender: permette di eseguire delle operazioni prima che il segue venga mostrato a video (tipicamente viene utilizzato per passare dei parametri o delle informazioni al controller invocato).

Questo codice, però, ha diversi problemi. Per prima cosa gli identifier dei vari controller sono hard-coded, ovvero sono delle stringhe fisse all’interno del codice, con tutti i problemi del caso (possibili errori di digitazione, poca manutenibilità del software, etc). Una soluzione spesso utilizzata per risolvere (parzialmente) il problema consiste nel definire un enumerativo che contenga i vari segue gestiti via codice:

enum SegueIdentifier : String {
   case ShowRed = "ShowRed"
   case ShowBlue = "ShowBlue"
   case ShowMagic = "ShowMagic"
}

Vediamo ora come migliorare questo metodo.

“Protocol Oriented Programming” is the way

Con Swift 2 è stata introdotta la “Protocol Oriented Programming” che, tra le varie novità che introduce, permette di definire protocolli che abbiamo già della logica definita ed implementata al loro interno. Nella sessione 411 della WWDC 2015, Apple ha mostrato come utilizzare la protocol oriented programming per migliorare la gestione dei segue. Vi consiglio di dare un’occhiata al video, contiene molti altri spunti interessanti!

Il protocollo che risolverà qualsiasi nostro problema è il seguente:

protocol SegueHandlerType {
    typealias SegueIdentifier: RawRepresentable
}

extension SegueHandlerType where Self: UIViewController, SegueIdentifier.RawValue == String {
    
    func performSegueWithIdentifier(segueIdentifier: SegueIdentifier, sender: AnyObject?) {
        performSegueWithIdentifier(segueIdentifier.rawValue, sender: sender)
    }
    
    func segueIdentifierForSegue(segue: UIStoryboardSegue) -> SegueIdentifier {
        guard let identifier = segue.identifier, segueIdentifier = SegueIdentifier(rawValue: identifier) else {
            fatalError("Invalid segue identifier")
        }
        return segueIdentifier
    }
}

Vediamo di analizzarlo nei suoi punti chiave. Per prima cosa, definiamo un protocollo SegueHandlerType, in cui al suo interno dichiariamo un typealias SegueIndentifier, che ci servirà per definire gli identificativi dei segue da gestire.

Fatto ciò, creiamo un’estensione per il protocollo (che, al momento, è ancora del tutto vuoto). L’estensione ci permette di specificarlo e dettagliarlo:

  • Il protocollo potrà essere applicato solo a classi di tipo UIViewController (o che derivino da questa)
  • Il typealias SegueIdentifier che abbiamo definito in precedenza dovrà avere come valore base il tipo String

I due metodi che vengono definiti dovrebbero già sembrarvi familiari. Il primo ci permette di invocare un segue utilizzando SegueIdentifier come parametro. Il secondo metodo, segueIdentifierForSegue:, merita invece più attenzione. Questo metodo riceve come parametro un oggetto UIStoryboardSegue, da cui andremo a ricavare l’identificativo del segue che sta per essere visualizzato. Utilizzando il nuovo statement guard, eseguiamo l’unwrapping dell’identificativo e convertiamolo in un valore di SegueIdentifier. Se queste due operazioni hanno successo (quindi il segue ha un identificativo valido che viene gestito nel nostro codice) il metodo ritorna il valore definito, altrimenti il metodo lancia un errore che causerà il crash della nostra applicazione. Questa può sembrare una forzatura, ma vi permetterà di scrivere codice più sicuro e robusto, gestendo tutti i segue della vostra applicazione.

SegueHandlerType in azione!

Vediamo ora un esempio di come utilizzare questo protocollo.

class HomeViewController: UIViewController, SegueHandlerType {

    enum SegueIdentifier : String {
        case ShowRed = "ShowRed"
        case ShowBlue = "ShowBlue"
        case ShowMagic = "ShowMagic"
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }

    // MARK: - Navigation

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        switch segueIdentifierForSegue(segue) {
        case .ShowRed:
            // logic here
        case .ShowBlue:
            // logic here
        case .ShowMagic:
            // logic here
        }
    }
    
    // MARK: - Actions
    
    @IBAction func doSomethingMagic() {
        performSegueWithIdentifier(.ShowMagic, sender: nil)
    }
}

L’enumerativo SegueIdentifier contiene la definizione di tutti i segue associati al controller. Ad ogni valore è associato l’esatto identificativo utilizzato nello storyboard, che verrà utilizzato dal metodo segueIdentifierForSegue: per ricavare il corretto enumerativo.

All’interno del metodo prepareForSegue:sender: sarà sufficiente richiamare il metodo segueIdentifierForSegue: e verificare il valore ritornato. Utilizzando uno switch, inoltre, Swift ci obbligherà a gestire tutti i casi possibili, aiutandoci così a realizzare un codice robusto e affidabile.

Il metodo doSomethingMagic, infine, mostra come invocare uno specifico segue utilizzando l’apposito metodo definito sempre nel protocollo.

That’s all folks! Una volta provato questo metodo di gestione dei segue vi assicuro che non ne potrete più fare a meno :]

Trovate il protocollo SegueHandlerIdentifier in questa repository di GitHub, mentre a questo link potete (e dovete!) vedere il video della sessione di apple alla WWDC 2015.

Ingegnere informatico e sviluppatore freelance, mi occupo da anni di sviluppo per iOS (ma non solo). Dal 2008 scrivo su questo piccolo blog (con qualche lunga pausa), in cui parlo di programmazione e di qualsiasi altra cosa che mi diverta.

Leave a reply:

Your email address will not be published.

Site Footer