Il framework SwiftUI. Cos’è? Il nuovo modo di dichiarare le interfacce in Xcode

Il framework SwiftUI cos'è? Il nuovo sdk per lo sviluppo di interfacce apple con xcode

Diciamolo francamente, sviluppare app, da quando esiste il linguaggio Swift è diventato un gioco da ragazzi.

Se gli aggiungiamo anche la presenza di Storyboard, Xib, Auto Layoutcosa vogliamo di più dalla vita noi poveri developers?

La risposta, oggi è semplicissima, SwiftUI.

Cos’è SwiftUI?

SwiftUI è il nuovo framework per lo sviluppo delle User Interface pensato per sostituire UIKit (o come complemento).

E se ti stai chiedendo perché Apple sentisse la necessità di tirar fuori un nuovo framework, le risposte sono molteplici e ti faranno tutte rimanere a bocca aperta.

Ora, prima di giocare al lancio del mac, fermati e guarda questo pezzo di codice. Poi confrontalo con l’equivalente UIKit e ti renderai conto che forse è il momento di passare a SwiftUI:

Ora, un bel respiro, e cominciamo ad analizzare gli aspetti principali di SwiftUI.

Cominciamo!

Live Rendering

Se con UIKit sembrava andare tutto per il meglio, bastava mettere piede fuori i confini Apple per vedere come il mondo dello sviluppo si stava muovendo da tutt’altra parte.

React Native e Flutter sono solamente due degli esempi più noti. E ti cito questi, perché oltre ad essere framework multipiattaforma hanno in comune un fattore che li rende altamente competivi (e famosi).

Il live rendering dei componenti (view).

Cioè quel processo che permette di compilare ed iniettare un cambiamento in maniera pressoché instantanea nell’app in esecuzione (si, non è fantascienza).

Modifichi il testo di un bottone, la forma etc e subito lo vedi comparire nell’app. Muovi un componente ed eccolo spostato anche in live…

In pratica hanno eliminato tutti quei tempi morti causati dal processo di: compilazione, building e attesa del simulatore (o meglio, avvengono ma non li noterai).

Se con UIKit tutto ciò è assolutamente impensabile, con swiftUI diventa realtà.

Ecco un assaggio:

La live preview vista su Xcode non è un rendering degli elementi, è proprio l’applicazione in run (come se fosse il simulatore, ma direttamente dentro Xcode).

La tecnologia utilizzata è una features del linguaggio Swift che si chiama Dynamic Replacement.

🔥 Per poter usare la live preview devi aver installato macOS 10.15.

Swift puro

Il core di UIKit è interamente scritto in Objective C. Noi sviluppatori Swift usiamo un layer che ci permette di interagirci tramite api swift friendly.

Questo processo porta inesorabilmente a scrivere tantissimo codice boilerplate.

Cioè codice che prescinde dalle funzionalità in sé ma serve solamente a far funzionare il componente (pensa, per esempio, ai protocolli UITableViewDataSource e Delegate della UITableView).

SwiftUI architettura

SwiftUI è un framework scritto interamente con il linguaggio Swift e scollegato da UIKit. Questo significa che ha nuove logiche e nuovi paradigmi di sviluppo.

Compatibilità con UIKit?

Si! Puoi usarli entrambi nello stesso progetto. L’uno non prescinde l’altro. Ovviamente metterli in comunicazione, dato che usano paradigmi differenti (vedi sotto) potrà comportare un po’ di mal di testa (ma si può fare).

UIKit deprecato?

No, per il momento. Significherebbe la ribellione ed Apple non vuole questo.

Però, probabilmente, tra qualche anno (molti) SwiftUI sarà la tecnologia di punta.

Declarative programming

Pur con tutte le tecnologie, framework, paradigmi e pattern continuiamo a programmare come programmavano i nostri nonni, cioè in modo Imperativo.

L’Imperative Programming definisce il cambiamento di stato di un elemento in maniera rigorosa. Da un’istruzione A sappiamo che ne deriverà sempre B.

Per farti un esempio:

var list: [User]

list = getList()

if (list.isEmpty) {
    showErrorAlert()
} else {
    refreshTableView() // chiama il cellForRow
}

func cellForRowAt(....) {
    let model = list[indexPath.row]
    cell.label.text = model.name
    cell.label.textColor = UIColor.black
    cell.image = model.image // lo stato dell'oggetto cell è mutabile
    //
    //
    return cell
}

Tutto è definito tramite “Ordini”. Se succede questo allora devi fare questo.

Con l’Imperative Programming si definisce il “come” ed il “perché” un oggetto dovrà cambiare.

Se non c’è, apparentemente, nulla di sbagliato in questo approccio (non starò qui a parlare di pregi e difetti) dall’altro lato la Declarative Programming cambia totalmente le carte in tavola.

Per la Declarative Programming lo stato di un oggetto non è definito a priori ma è solamente dichiarato.

Che significa? 🤔

Noi consigliamo ad un componente come questo dovrà apparire ma non definiamo mai il come ed il perché.

Per riproporti l’esempio di prima:

@State var model = Users.listModel

List(model.list) { item in
    Image(item.image)
    Text(item.name)
      .fontWeight(.light)
      .color(.black) // l'oggetto Text è immutabile, viene ridefinito ad ogni modifica
}

In pratica, abbiamo consigliato alla List (l’equivalente della UITableView) di mostrare gli elementi presenti dentro model.list. Ad ogni elemento chiediamo di creare una row con un’Image (simil UIImageView) ed un elemento Text (simil UILabel).

Declarative Programming: si definisce il “cosa” dovrà essere ma non il “come” ci dovrà diventare.

Multipiattaforma

SwiftUI è multipiattaforma (Apple si intende). Dato l’accesso diretto ai componenti nativi ed a delle nuove API, ci basterà definire il come dovranno essere gli elementi (Declarative Programming) e poi sarà il framework a trasformarli nei componenti nativi.

Quindi, una sola vera code base ed una compilazione multipiattaforma.

Xcode

Con il framework SwiftUI cambiano anche i tool di Xcode. Preparati a salutare definitivamente lo Storyboard, l’Auto Layout e le Size Class.

In particolare, proprio lo Storyboard non esisterà più (quanti mila likes per questa news?)

Ogni componente, View, avrà una live preview a se stante. Quindi, definirai i componenti in un file e li comporrai in altri.

Questo vuol dire che le Navigation (i vecchi segue per intenderci) dovranno essere gestiti da codice.

l’unico svantaggio è il non poter vedere tutto il percorso dell’app come accadeva con lo Storyboard.

ma dato gli enormi benefici di SwiftUI, pazienza 😂.

Considerazioni

Con il framework SwiftUI cambia totalmente il modo di sviluppare le applicazioni.

Dopo il linguaggio Swift e l’Open Source questa rappresenta la terza svolta nel reparto sviluppo di mamma Apple.

Sintomo che finalmente ha deciso di ascoltare la sua platea di sviluppatori che, da qualche anno ormai, lamentava servizi un po’ scadenti e tecnologie obsolete (pensiamo ai crash continui di Xcode, Storyboard impallati, versioning impossibile etc).

Urliamo ad alta voce, benvenuto swiftUI.

Documentazione

Prima di lasciarti, ti lascio i link alla documentazione ufficiale del framework swiftUI:

Buona Programmazione!

Come installare e usare Cocoapods. Velocizza lo sviluppo delle tue applicazioni iOS!

Come installare e usare Cocoapods

La condivisione, o Open Source, è ciò che ha reso e sta rendendo grande la programmazione dei giorni nostri.

Lo sappiamo! sviluppare un’app, sopratutto all’inizio, può essere un vero e proprio calvario.

Spesso vogliamo realizzare interfacce o funzionalità complesse. Vogliamo aggiungere quel quid in più che rende il nostro prodotto diverso e sempre competitivo.

Chi non lo vorrebbe?

Però, nella maggior parte dei casi si finisce per non avere le competenze o per non avere il tempo necessario a crearle.

Peppe, che si fa? scendiamo nella mediocrità o proviamo il passo più lungo?

Niente di tutto ciò.

Negli ultimi anni sono tantissimi gli sviluppatori che amano condividere il proprio lavoro, il proprio codice e le proprie soluzioni con il mondo intero.

Cercando in rete avrai sicuramente notato due siti saltar sempre fuori:

  • Stack Overflow: Gli sviluppatori pongono delle domande e, generalmente, ricevono delle risposte con la soluzione al problema.
  • Github: Vengono condivisi interi codici, framework ed app con l’intento di diffonderli gratuitamente e ricevere in cambio un aiuto nel mantenerli.

Da questi due siti puoi tirar fuori di tutto. Io li uso tutti i giorni per informarmi, vedere come fanno gli altri, per poi applicare ai miei progetti.

Quindi, che devo fare?

Non devi reinventare la ruota (anche se sono un po’ contrario a questo pseudo-principio).

Però, quando andiamo di fretta, il riutilizzo può essere una manna dal cielo nel velocizzare i nostri processi di sviluppo.

Ecco perché, oggi, voglio mostrarti come utilizzare il codice prodotto dagli altri nel tuo progetto iOS.

Lo faremo sfruttando CocoaPods.

CocoaPods?

Allaccia le cinture perché stai per entrare nel fantastico mondo dei Pods.

Cos’è CocoaPods?

Cocoapods è un’app per la gestione delle dipendenze dei nostri progetti. In pratica, ci permetterà di installare ed aggiornare automaticamente i framework degli altri sviluppatori.

Dietro le quinte è un aggregatore di risorse gratuite condivise da chi ha buon cuore.

Nel sito web, infatti, potrai sfogliare questa enorme libreria utilizzando la funzione di ricerca:

Cos'è cocoa pods e come integrarlo nelle nostre app ios
https://www.cocoapods.org

I Pods, come vengono chiamati, spaziano dalla grafica fino alle funzionalità più stravaganti ed utili.

Per esempio, potrai integrare un menu particolare come quello a scorrimento laterale, un bottone con stile material, una fotogallery, un network layer e così via.

Cos’è un Pod?

Un Pod è, nella maggior parte dei casi, un framework.

Cos’è un framework?

È un insieme di classi, strutture etc per la risoluzione di un problema. Una sorta di “cartella” con dei file che potrai importare nel tuo progetto.

Se vuoi approfondire, ho spiegato cos’è un framework e come crearlo in questo tutorial:

Quindi, in definitiva, CocoaPods ti permetterà di installare ed aggiornare automaticamente i framework, prodotti dagli altri sviluppatori, all’interno del tuo progetto Xcode.

Come installare CocoaPods

Installare CocoaPods è semplicissimo. Apri il terminale del tuo Mac e digita:

sudo gem install cocoapods

Premi invio.

Con questo comando, dai istruzione di scaricare ed installare quest’applicazione. Se hai impostato una password per il tuo mac, ti verrà richiesta.

Nel caso i comandi del terminale ti suonassero come nuovi:

  • sudo: comando per i permessi di root/amministrazione del mac.
  • gem install è il comando che permette di installare gemme (“applicazioni”) scritte in Ruby sul proprio Mac (Ruby è installato di default in tutti i Mac).
  • cocoapods: Infine, stai chiedendo al manager di Ruby di installare cocoapods.

Come aggiungere un Pod al tuo progetto

Una volta installato CocoaPods, adesso potrai aggiungere i Pods ai tuoi progetti (nuovi o vecchi che siano).

Prima di tutto, naviga con il terminale fino alla cartella del tuo progetto. Nel mio Mac, il progetto d’esempio l’ho messo in /Documents/Progetti/CocoaTestApp.

Quindi, io digiterò il comando cd seguito dal path:

cd /Users/giuseppe/Documents/Progetti/CocoaTestApp
  • cd: sta per change directory e serve per entrare in una directory del Mac.

PRO TIP: Nel caso dovessi avere difficoltà a trovare il percorso della cartella del tuo progetto, puoi trascinarla nel terminale ed il percorso verrà scritto in automatico.

Adesso digita il comando pod init per inizializzare CocoaPods:

pod init

Ora vai nella cartella del progetto, dovresti trovare un file chiamato PodFile.

Apri il PodFile con un editor di testo. Io uso Atom, che è un editor di testo gratuito, ma puoi usare tranquillamente il Text Edit.

PodFile cocoa pods xcode ios

Target – do – end

Il blocco targetdoend sta ad indicare in quale target della tua app verranno installati i pods.

Tecnicamente dentro un PodFile puoi aggiungere diversi di questi blocchi in modo tale da installare i pods solamente dove necessario.

Esempio?

Se mi serve un pod solamente nel Today Extension, allora aggiungerò un nuovo target – do – end a tale scopo.

Alamofire

Il pod che voglio farti installare si chiama Alamofire ed è un framework che ti aiuterà nella scrittura di richieste HTTP.

Non entrerò nei dettagli di Alamofire perché non è il focus di questo tutorial. A noi interessa semplicemente come installare un framework usando cocoapods.

Generalmente i framework hanno un Readme (le istruzioni) nella pagina principale di Github. Se non lo hanno è già un campanello d’allarme (ti consiglio di non installare il pod).

Nel caso di Alamofire, se scorri nel paragrafo dedicato all’installazione dice, per quanto riguarda CocoaPods, di scrivere la seguente riga:

pod 'Alamofire', '~> 5.0.0.beta.1'

Dove devo aggiungere quest’istruzione?

La devi inserire dentro il blocco target – do – end. Nel mio caso, il file PodFile, sarà il seguente (se copi ed incolli ricordati di cambiare il nome del target e la versione di Alamofire dato che potrebbe essere stato aggiornato):

target 'CocoaTestApp' do
  use_frameworks!

  pod 'Alamofire', '~> 5.0.0.beta.1'

end

Posso aggiungere più pod?

Ovviamente si!

Ti basterà aggiungere una nuova riga con il nome del pod da installare. Per esempio, se volessi aggiungere Firebase insieme ad Alamofire il mio PodFile diventerà questo:

target 'CocoaTestApp' do
  use_frameworks!

  pod 'Alamofire', '~> 5.0.0.beta.1'
  pod 'Firebase'

end

Firebase è il SaaS di Google: Ti permette di aggiungere velocemente Database, Login, Analytics etc alla tua app. Ne ho parlato in questo tutorial introduttivo.

Installare i Pods

Salva il PodFile. Torna al terminale e digita il comando pod install:

pod install

Dovrebbe scaricare tutti i pods inseriti ed installarli all’interno del tuo progetto.

A termine del processo, nella cartella dovrebbero essere comparsi dei nuovi file e cartelle:

installazione pod e xcworkspace

Nel particolare dovresti trovare:

  • La cartella Pods che conterrà i framework scaricati
  • Il file con estensione xcworkspace che dovrai usare per aprire il progetto.

Da ora in avanti dovrai aprire il progetto con il file xcworkspace. In questo modo potrai utilizzare correttamente i Pods scaricati.

Aperto il workspace, potrai utilizzare il pod come un normalissimo framework (quindi con l’import nel file etc). Se non ti ricordi come si fa, puoi leggere il tutorial in cui ho spiegato come si creano ed utilizzano.

Aggiornare i Pods

Dopo un po’ sicuramente ti capiterà di voler aggiornare i pods. Generalmente accadrà o perché non sono più compatibili con iOS/watchOS/etc oppure perché presentavano dei bug.

Per aggiornare i pod ti basterà digitare il comando pod update all’interno della cartella del progetto:

pod update

Prima di farlo, ti consiglio di controllare sempre la documentazione ed i changelog dei framework. In questo modo potrai decidere meglio se aggiornare o meno.

Platform

Se hai notato, nel PodFile in alto c’è un’istruzione che è commenta e riguarda la versione di iOS:

platform :ios, 'x.x'

Se togli l’asterisco, che indica la non esecuzione, abiliterai la versione di compatibilità minima dei pods con iOS.

Così facendo, durante un pod update o pod install verranno scaricati i pod compatibili con quella versione o le successive di iOS.

Nel caso in cui stessi supportando diverse piattaforme, ti basterà aggiungere altre istruzioni platform al tuo podfile.

Versione dei Pods

Se dovessi aver bisogno di installare una versione ben specifica, dopo il nome del pod, metti una virgola ed aggiungi una stringa con la versione desiderata:

pod 'Firebase/Firestore', '5.10.1'

Se invece vuoi installare delle versioni definendo delle specifiche più complesse, puoi usare i seguenti simboli:

  • ‘= 1.2.0’ installerà esattamente la versione 1.2.0 (il numero è un esempio). L’equivalente del comando visto sopra.
  • ‘> 1.0.0’ le versioni successive alla 1.0.0. Puoi aggiungere anche il ‘>=’ per includere anche l’estremo inferiore.
  • ‘<‘ o ‘<=’ seguiti dalla versione, fanno l’opposto del comando precedente.
  • ‘~> 5.0.0’ installerà le versioni superiori alla 5.0.0 fino alla versione 6.0.0. Come se fosse un >= e <= di una versione solo che l’estremo superiore è definito partendo dall’incremento di una unità sull’inferiore.
pod 'Firebase/Auth', '> 5.0.0' // qualsiasi versione superiore alla 5
pod 'Firebase/Auth', '<= 5.0.0' // qualsiasi versione inferiore o compresa la 5
pod 'Firebase/Auth', '~> 5.0.0' // dalla 5 alla 6

Disinstallare i pods

Una delle cose più fastidiose riguardo CocoaPods è quella legata alla rimozione di uno o più pods.

Così è praticamente impossibile.

Allora, come fare?

Dovrai utilizzare una gem esterna che si chiama cocoapods-deintegrate. Quindi, installala utilizzando il comando:

sudo gem install cocoapods-deintegrate

Completata l’istallazione, spostati con il cd nel progetto incriminato ed avvia il comando deintegrate:

pod deintegrate

Così facendo verranno rimossi tutti i pods ed il progetto tornerà alla forma base. All’interno della cartella ritroverai solamente il PodFile.

A questo punto rimuovi dal PodFile i pod che non ti servono, salva e re-installa i pod rimasti con il comando pod install.

Semplice, veloce ed indolore.

La lista dei migliori Framework

Dove trovo i pods e framework per la mia app?

Qualcuno ha ben pensato di creare una lista con i migliori framework in circolazione.

La repo Awesome iOS contiene un’infinità di framework ordinati per tipologia. Viene costantemente aggiornata, quindi salvati il link tra i preferiti perché è un must have.

Conclusione

Prima di lasciarti, voglio darti un suggerimento.

CocoaPods, come anche altri strumenti (Carthage o lo Swift Package Manager), aggiungono delle dipendenze esterne al tuo progetto.

Come tutte le dipendenze, se assunte in eccesso, possono portare a dei veri e propri disastri.

Peppe, che vuoi dire?

I disastri avvengono per un motivo semplicissimo: molti framework potrebbero non essere aggiornati di anno in anno.

In questa situazione potresti decidere di dover rimuovere il framework o, se è proprio necessario e questo è open source, dovrai rimboccarti le maniche per risolvere i suoi problemi.

Quindi, fai molta attenzione 😂

Il mio consiglio è di installare solamente quelli necessari, ben mantenuti (basta vedere la frequenza di aggiornamenti) e con un’ottima documentazione.

Ti lascio il link all documentazione ufficiale di CocoaPods.

E, se ti fa piacere, ricordati di seguirmi anche su Youtube ed Instagram dove rilascio tutorial settimanalmente e piccole tips (su IG).

Buona Programmazione!

Encode e Decode di JSON con il protocollo Codable. Deserializzare le API di wordpress con Swift

Encode e Decode di JSON con il protocollo Codable

Il JSON (JavaScript Object Notation) è il formato file più in voga nella trasmissione dei dati tra un’app ed un servizio. Sono leggeri, facili da comporre e, cosa non da poco, si possono decodificare nei tipi di dato utilizzati dai nostri linguaggi nativi (tra cui anche Swift).

Se fino ad oggi deserializzavi un JSON trasformandolo in un generico Dictionary o Array, come ho spiegato qui, beh sappi che tutto sta per cambiare (in positivo per fortuna 😂).

Grazie alle ultime versioni del linguaggio Swift (dalla 4 in poi) è possibile trasformare un Data, contenente un JSON, in un oggetto che lo rappresenta.

Il tutto possibile grazie a tre nuovi attori, aggiunti nello Swift Standard Libray, che sono: l’Encoder, il Decoder, ed il type Codable:

decodificare un file JSON con il linguaggio Swift

Quale stregoneria è mai questa? 😱

Procediamo con ordine.

Un file JSON ha una struttura semplicissima. Nella sua forma base può essere un Array, un Dictionary o una composizione tra i due. Ogni oggetto, delimitato da graffe, è un insieme di chiave-valore. 

Le chiavi del JSON sono delle stringhe mentre i valori cambiano in base al tipo di dato trasportato. Generalmente, i valori associati sono Number, String, URL, Date, Array, Dictionary etc (cioè tutti i tipi di dato che possono essere rappresentati da stringhe o numeri).

Da questa struttura lineare e prevedibile, uno strumento chiamato decoder è in grado di mappare le chiavi di un oggetto JSON nelle proprietà di un oggetto nativo che lo rappresenta. Cioè, se hai una chiave chiamata name ed una class/struct che ha name come proprietà, il decoder metterà il valore della chiave nella proprietà del tuo oggetto.

Ed, ovviamente, grazie ad un encoder potrò eseguire il processo inverso.

Ci sei qui?

Allora vediamo l’encodedecode di un JSON con il protocollo Codable. Vedremo come usarlo con due esempi:

  1. Nella prima parte: deserializzeremo le API fornite da swapi.co, che fornisce le informazioni base sull’universo di Star Wars (ed è ottimo per impratichirsi con i JSON e la decodifica).
  2. Poi ti mostrerò come decodificare le API di WordPress e ti mostrerò alcuni casi complessi d’utilizzo del protocollo Codable.

Cominciamo! 😎

Postman

Inanzitutto scarica l’applicazione gratuita Postman da qui. Postman è un’app nativa che ti permetterà di eseguire richieste HTTP in maniera semplice e veloce.

Con Postman potrai fare tantissime operazioni, noi lo useremo per richieste di tipo GET a servizi che restituiscono file JSON:

come usare postman richieste get di json

L’URL che utilizzeremo, per il primo esempio, è quello fornito dal servizio swapi.co che contiene tantissime informazioni sull’universo di Star Wars. Ed in più, è un ottimo strumento per esercitarsi su questi argomenti dato che i suoi JSON sono semplici ed intuitivi.

Quindi, inserisci questo URL su Postman: https://swapi.co/api/people/1 (restituirà un JSON con le info di Luke Skywalker).

Se dovessi avere dei problemi, scrivimi pure un commento in fondo alla lezione.

Il typealias Codable

Mano al Playground. Una volta letto il JSON con Postman, o altri programmi, il primo passo da fare è quello di creare una classe o struttura che lo rappresenti.

Dato che l’URL che stiamo utilizzando restituisce le informazioni di Luke Skywalker (api/people/1), o più in generale degli attori (basta cambiare il numero alla fine), chiameremo la nostra struttura StarWarsUser.

Infine, la tua class/struct la devi rendere conforme al protocollo Codable. 

import UIKit 

struct StarWarsUser: Codable { 

}

Cos’è il Codable?

Codable è un typealias introdotto nella versione 4 del linguaggio Swift ed è presente all’interno della Swift Standard Library (Foundation).

typealias Codable = Decodable & Encodable

Un typealias è il sistema che ci permette di definire un nuovo tipo partendo da altri tipi. Nel caso del Codable è l’intersezione tra il protocollo Decodable ed il protocollo Encodable.

Decodable ed Encodable

Cosa fanno il Decodable e l’Encodable?

  1. Decodable: permette di decodificare una rappresentazione esterna, come un JSON, in una rappresentazione interna (da JSON alla class/struct conforme a questo protocollo).
  2. Encodable: permette la codifica di una rappresentazione interna, un nostro oggetto, in una rappresentazione esterna come può essere un JSON.

Rendendo la classe conforme al Codable sarà automaticamente possibile codificare e decodificare i suoi oggetti in JSON o viceversa.

Nell’eventualità ti servisse solamente una delle due caratteristiche puoi tranquillamente rendere la classe conforme o solamente al protocol Decodable o solamente al protocol Encodable.

E adesso che si fa?

Chiavi e proprietà

Una volta creata la struttura o classe è arrivato il momento di inserire le sue proprietà.

Quali mettere e di che tipo?

Nel caso del JSON di Luke Skywalker, al suo interno troviamo diverse coppie chiave-valore. Immaginiamo di voler prendere solamente alcuni di quei dati: il name ed il films (si, puoi anche non mapparle tutte).

Individuate le chiavi, dovrai inserire all’interno della struct/class delle proprietà con lo stesso identico nome:

struct StarWarsUser: Codable {
    let name
    let films
}

Manca ancora una cosa.

Di che tipo devono essere le nostre proprietà?

Ti dicevo che i valori di un JSON sono pochi e sempre gli stessi. Le stringhe sono identificate da una coppia di doppie virgolette “”,  i numeri si riconoscono, gli array da [] e gli oggetti da {}.

Quindi nel caso delle chiave name, il type sarà String.

Per quanto riguarda films, qui è più complesso.

Si tratta di un array, quindi [] che al suo interno contiene dei valori String.

Potremmo anche usare il type [String] ma, con il linguaggio Swift, dato che all’interno dell’array films ci sono degli URL possiamo utilizzare il type URL.

struct StarWarsUser: Codable {
    let name: String
    let films: [URL]
}
Mappare chiavi valore JSON Codable linguaggio swift

I type permessi

Posso utilizzare tutti i tipi di dato?

No.

Solamente quelli che, a loro volta, sono conformi al protocollo Codable. 

I tipi che potrai utilizzare sono i fondamentali, quindi: Int, Double, String, Float, Array e Dictionary. Più Date, CGPoint, URL…

Se non sei sicuro che un type di swift non sia conforme al Codable ti basta dare un’occhiata alla documentazione (esempio – nella parte finale sono elencati i protocolli a cui è conforme il type).

La cosa più figa?

Potrai utilizzare le tue struct/class come type delle tue proprietà e queste verranno decodificate e codaficate in automatico (ma questo esempio lo vedremo tra poco).

JSONDecoder

Ci siamo! Abbiamo tutto pronto per cominciare a decodificare il nostro JSON. 

Lo faremo sfruttando l’oggetto JSONDecoder che trasformerà un Data in un oggetto conforme al protocollo Decodable. 

Per il download del data, sfrutteremo il metoto Data.init(contentsOf: URL) per evitare di mettere complessità al tutorial. In un progetto reale, generalmente, utilizzerai le URLSessionDataTask o qualche bel framework (come Alamofire).

let data: Data = try! Data.init(contentsOf: URL.init(string: "https://swapi.co/api/people/1")!)

Nota come ho tralasciato la possibilità di sollevare un’eccezione, utilizzando il try!, ed ho wrappato forzatamente l’URL (non è l’obiettivo di questo tutorial, lascio a te il compito di utilizzare il metodo di download più congeniale).

Ottenuto il Data, adesso devi creare il JSONDecoder:

let decoder: JSONDecoder = JSONDecoder.init()

Il decoder ti fornisce un metodo chiamato .decode(Decodable.Protocol, from: Data) -> Decodable. 

Questo accetta due parametri:

  1. Un type conforme al protocollo Decodable. Qui dovrai scrivere il nome della tua struct/classe seguita dal .self
    1. Esempio: StarWarsUser.self
  2. Il Data da cui estrarre il JSON.

Ed, infine, restituisce un oggetto Decodable istanza del type passato al metodo. L’oggetto sarà valorizzato con i dati presenti nel Data.

Se la decodifica non avviene con successo il metodo solleverà un’eccezione. Quindi, la chiamata al metodo decode, deve essere wrappata all’interno di un blocco do-try-catch.

Se tutto va per il verso giusto, dovresti avere un oggetto StarWarsUser con i suoi attributi valorizzati. Ti lascio il codice completo:

import UIKit

struct StarWarsUser: Codable {
    let name: String
    let films: [URL]
}

let data = try! Data.init(contentsOf: URL.init(string: "https://swapi.co/api/people/1")!)

do {
    let decoder: JSONDecoder = JSONDecoder.init()
    let user: StarWarsUser = try decoder.decode(StarWarsUser.self, from: data)
    
    print("Ciao, \(user.name)")
    print("Hai partecipato a \(user.films.count) films")
    
} catch let e {
    print(e)
}
JSONDecoder con linguaggio swift

Pazzesco no? soprattutto se lo paragoniamo al vecchio sistema 😍.

JSONEncoder

Capito il Decoder, il JSONEncoder è altrettanto semplice.

Una volta costruito un oggetto encoder, dovrai utilizzare il suo metodo .encode(Encodable) che restituirà un Data, il quale conterrà il JSON dell’oggetto passato al parametro:

do {

    let user: StarWarsUser = StarWarsUser.init(name: "Luke", films: [])
    let encoder: JSONEncoder = JSONEncoder.init()
    let data: Data = try encoder.encode(user)

} catch let e {
    print(e)
}

Anche l’encode può sollevare eccezioni, quindi dovrai inserirlo in un blocco do-try-catch.

API WordPress

Visti l’Encode e Decode di JSON con il protocollo Codable, adesso è arrivato il momento di cominciare a scendere in qualche esempio più complesso.

Sfrutterò le API di WordPress, in modo tale da poterti spiegarti alcuni concetti avanzati legati alla decodifica e codifica che ti torneranno molto utili nei tuoi progetti.

Perché le API di WordPress?

WordPress è alla base di circa il 25% di tutti i siti web. Le sue API, se non bloccate, sono pubbliche nei siti basati su questo CMS.

Per esempio, ho sfruttato le sue API per realizzare l’app di iSpazio (il sito è basato su WordPress, ma abbiamo cambiato il path d’accesso per evitare abusi).

Nell’app, nelle ultime versioni, grazie al protocollo Codable sono riuscito a ridurre il codice di decodifica di circa il 40%.

La documentazione ufficiale delle API di WordPress la trovi a questo indirizzo https://developer.wordpress.org/rest-api/.

Di base, per aiutarti a comprenderla, un sito web ti permette l’accesso alle sue API utilizzando il path generico: https://www.nomesito.dominio/wp-json/wp/v2.

Ultimi posts

Dal path generico puoi accedere alle varie risorse. Nel caso in cui volessi prendere gli ultimi post pubblicati, dovrai aggiungere un /posts/ alla fine del path.

let data = try! Data.init(contentsOf: URL.init(string: "https://www.xcoding.it/wp-json/wp/v2/posts")!)
decodifica API di WordPress con linguaggio swift xcode ios

Il JSON restituito è composto da un array di 10 oggetti. Ogni oggetto dell’array ha la stessa struttura e questi contengono coppie di chiave-valore che descrivono il post in questione.

Questo presuppone che, come nel caso dell’array di films visto nell’esempio precedente, potremo creare un array di oggetti Post.

procediamo con ordine dato che è facile confondersi quando ci ritroviamo di fronte a JSON del genere.

La struct WordPressPost

Abbiamo detto che dentro l’array ci sono oggetti tutti uguali. Quindi, vuol dire che potremo rappresentarli con una struct/class conforme al Codable.

Crea una struct, chiamata WordPressPost, ed al suo interno inserisci le proprietà id e link. L’id sarà un Int, nel JSON è un numero, ed il link un URL.

Consiglio: non partire mai in quarta, scrivi una struct/class semplice e poi prova la decodifica:

struct WordPressPost: Codable {
    let id: Int
    let link: URL
}

Decodifica

Qui cominciano i primi problemi. Che tipo dobbiamo passare al decoder?

Nell’esempio di Star Wars, il path in questione restituiva un oggetto e quindi abbiamo utilizzato StarWarsUser.self.

Nel caso delle API di WordPress, e dei suoi posts, non puoi mettere WordPressPost.self perché significherebbe decodificare tutto il JSON in un WordPressPost. Quando, in realtà, il path restituisce un array di WordPressPost.

Questo sta a significare che il tipo da utilizzare è un array. Un array di WordPressPost:

do {
    
    let decoder = JSONDecoder.init()
    let posts: [WordpressPost] = try decoder.decode([WordpressPost].self, from: data)
    
    print("Ecco gli ultimi \(posts.count) posts di xcoding.it")
    
} catch let e {
    print(e)
}

Ci sei fin qui?

In caso scrivimi, senza farti problemi, un commento in fondo alla lezione (anche io sono impazzito all’inizio 😅).

All’interno del decode devi sempre mettere il type del JSON restituito.

Il path posts, restituisce un array perché ci sono le [] all’esterno. Poi gli oggetti all’interno dell’array verranno decodificati con il type passato dentro le [].

Adesso, dentro la let posts ci saranno tutti i WordPressPost decodificati. 

Se fin qui non abbiamo aggiunto nulla di nuovo, le cose cominciano a complicarsi non appena proviamo a decodificare chiavi più complesso. 

Object Composition

Immaginiamo di voler estrarre il title dal JSON. La chiave title, se torni su postman, vedrai che è un oggetto composto da una sottochiave rendered di tipo String.

E, se fai ancor più attenzione, noterai che questa struttura si ripete anche in altre chiavi (guid, content etc):

Come fare?

Se hai seguito il corso gratuito Swift Tour sul linguaggio Swift, nel capitolo dedicato alla programmazione orientata agli oggetti, ho spiegato un principio chiamato Object Composition

Consiste nel poter creare classi o strutture composte da altre class/struct. Nel nostro caso, potremmo creare una struct chiamata WordPressText da utilizzare come type di una proprietà contenttitleguid etc:

struct WordPressText: Codable {
    let rendered: String
}

struct WordPressPost: Codable {
    let id: Int
    let link: URL
    let title: WordPressText
    let content: WordPressText
}

Qualora dovessi trovare JSON composti, il consiglio è di scomporre sempre ed utilizzare la Object Composition.

Se torniamo al nostro esempio, ora, per poter leggere il title o il content, ti basterà fare un post.title.rendered o post.content.rendered:

let decoder = JSONDecoder.init()
let posts: [WordpressPost] = try decoder.decode([WordpressPost].self, from: data)
    
print("Ecco gli ultimi \(posts.count) posts di xcoding.it")
    
for post in posts {
    print(post.title.rendered)
}

Optional

Prima o poi ti capiterà di incontrare JSON contenente delle coppie chiave-valore in cui quest’ultimo è null (il nostro nil, o addirittura essere totalmente assente anche la chiave).

In tutti questi casi, potrai rendere la tua proprietà di tipo opzionale. Così, quando il decoder proverà a decodificare quella chiave: se c’è metterà il valore, altrimenti passerà nil.

Nelle API di WordPress non c’è nessun valore di tipo null. Però, ci sono dei casi, come la chiave protected che trovi all’interno della chiave content, che troviamo in un posto ma non in altri.

Per esempio, la chiave protected è dentro excerpt e content ma non è dentro title e guid (a cosa possa servirci la protected onestamente non lo so, ma è utile ai fini di questo esempio). 

Siccome stiamo utilizzando la struct WordPressText per decodificare tutte le chiavi che hanno la proprietà rendered, nel caso in cui volessi utilizzare la stessa struct, e contemporaneamente decodificare la protected, puoi inserire la proprietà come opzionale:

struct WordPressText: Codable {
    let rendered: String
    let protected: Bool?
}

Quindi, per fare un recap, usa l’opzionale quando:

  1. Non sei sicuro che la chiave esista
  2. Il valore associato ad una chiave potrebbe essere null

Nested keys

Se tutto fosse semplice non ci sarebbe nessuna soddisfazione. Giusto? 😂

Ecco perché, prima di farti vedere un aspetto importante legato al Codable, voglio analizzare la decodifica di una proprietà complessa come potrebbe essere _links.

La key _links è una proprietà contenuta all’interno di un post di WordPress.

Contiene i link alle risorse “accessorie” legate ad un post. Per esempio, se volessi accedere alle informazioni dell’autore dovrei eseguire una nuova richiesta al link contenuto all’interno della proprietà href della key author (ti verrà restituito un JSON con le informazioni dell’autore: nome, immagine etc).

Qui abbiamo tre livelli di annidamento. Dentro _links ci sono degli oggetti che, a loro volta, contengono degli array di oggetti.

Come si deserializzano?

Si parte sempre dall’esterno. Quindi, aggiungi la proprietà _links alla struct WordPressPost.

Di che tipo deve essere?

Possiamo sfruttare la Object Composition e creare una nuova struct, chiamata WordPressPostLinks, che conterrà le proprietà self, collection, about etc.

struct WordPressPostLinks: Codable {
    
}

struct WordPressText: Codable {
    let rendered: String
    let protected: Bool?
}

struct WordPressPost: Codable {
    let id: Int
    let link: URL
    let title: WordPressText
    let content: WordPressText
    let _links: WordPressPostLinks
}

Adesso dovremmo aggiungere le proprietà dentro la WordPressPostLinks.

Sicuramente avrai notato che tutti gli oggetti (collection, about etc) hanno la stessa struttura. Cioè, tutti contengono un array di oggetti con chiave href.

Nuovamente, puoi creare una struttura, chiamata WordPressLinks, che avrà la proprietà href. Questa struct la useremo come tipo di dato degli array presenti dentro la WordPressPostLinks.

struct WordPressLinks: Codable {
    let href: URL
}

struct WordPressPostLinks: Codable {
    let collection: [WordpressLinks]
    let about: [WordpressLinks]
    let author: [WordpressLinks]
}

Ti prego, dimmi che sei ancora vivo 😂

A questo punto, devi solamente testare che tutto funzioni a dovere. Dovresti avere il seguente codice:

Nel caso in cui volessi avere tutte le informazioni, contenute dentro i _link, all’interno del JSON puoi aggiungere la query _embed alla richiesta.

L’URL diventerebbe il seguente: https://www.xcoding.it/wp-json/wp/v2/posts?_embed

CodingKeys

Ti sei chiesto perché non ho inserito la proprietà self all’interno della WordPressPostLinks?

Certe volte potresti trovare, all’interno dei tuoi JSON, delle chiavi che hanno nomi composti da parole riservate (come il caso di self) o da caratteri speciali (wp:featuredmedia, i : non si possono usare come nome delle proprietà in Swift).

Come possiamo risolvere?

All’interno della tua struct/class potrai definire un enum, di tipo String, nel quale creare le associazioni tra proprietà e chiavi speciali del JSON.

Questo enum dovrà essere conforme al protocollo CodingKey.

Nel nostro caso, dovrai inserire l’enum, che puoi tranquillamente chiamare CodingKeys, all’interno della struct WordPressPostLinks per decodificare la key _self e la wp:featuredmedia:

struct WordPressPostLinks: Codable {
    
    let collection: [WordpressLinks]
    let about: [WordpressLinks]
    let author: [WordpressLinks]
    let _self: [WordpressLinks]
    let featured_media: [WordpressLinks]
    
    enum CodingKeys: String, CodingKey {
        case collection, about, author
        case _self = "self"
        case featured_media = "wp:featuredmedia"
    }
}

Qui è importante capire due cose fondamentali:

  1. Devi comunque inserire i case con i nomi delle proprietà che funzionano (nell’esempio: collection, about, author)
  2. Devi creare n case per quante sono le proprietà “speciali”. Lì dovrai definire la stringa che verrà utilizzata per la decodifica.

Puoi usare questo sistema anche per definire associazioni tra chiavi e proprietà indifferentemente dal fatto che contengona caratteri speciali. Però, per abitudine e semplicità, utilizzo questo sistema quando incontro i problemi sopraccitati.

init from decoder

Da dov’è che parte la decodifica del JSON e la creazione del nostro oggetto?

Tutto parte dal costruttore. Con la conformità al protocollo Decodable, possiamo definire il comportamento dell’init richiamato al momento della mappatura del JSON.

L’init in questione è il seguente:

init(from decoder: Decoder) throws {
        
}

Peppe, che vantaggi ottengo dall’inserire questo init?

Dato che il costruttore porta con se il decoder, potrai interagire con quest’ultimo per “aiutarlo” a decodificare correttamente i tuoi dati.

Mi fai un esempio?

Il problema dei Type

Immaginiamo d’avere un JSON che contiene i dati di un utente. Per semplicità, questa volta userò una stringa che poi verrà comunque decodificata dal decoder:

let string = """
{
    "id": "1234",
    "name": "Giuseppe Sapienza"
}
"""

let data = string.data(using: .utf8)!

Quindi, si, puoi decodificare stringhe che contengono JSON. Se la sua struttura è corretta, una volta convertito in Data, potrai gestirlo come negli esempi visti sopra.

Torniamo a noi.

Per i fini della tua app hai la necessità e l’obbligo di trattare l’id come Int invece che String (o semplicemente perché il tuo PM si è svegliato così quel giorno 😅). Quindi, hai una struttura simile a questa:

struct User: Codable {
    var id: Int
    var name: String
}

Container

Non c’è CodingKey che tenga. Devi, per forza, intervenire sull’init.

Il primo step da fare è quello di aggiungere l’init nella struct. Successivamente dovrai recuperare il container dal decoder che è letteralmente il contenitore del JSON esaminato e quello che ti permetterà di recuperare i suoi valori:

init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: User.CodingKeys.self)
}

Per accedere al container bisogna utilizzare il metodo decoder.container(keyedBy: CodingKey) che accetta come parametro un type conforme al CodingKey (che gli permetterà di capire quali chiavi del JSON mappare).

Di default, se non lo inserisci manualmente, viene generato un enum CondingKeys “invisibile” a cui puoi accedere tramite la sintassi NomeStruct.CodingKeys.

il metodo .container può sollevare eccezioni. Dato che ci troviamo dentro un metodo marcato come throws puoi semplicemente utilizzare il try prima dell’invocazione.

In questo modo l’errore verrà propagato all’esterno.

Container.decode

Infine, dal container, potrai estrarre i valori utilizzando il metodo container.decode(Type, forKey: CodingKeys) che accetta due parametri:

  1. Il Type del valore presente nella chiave che vuoi decodificare. Deve essere un type conforme al protocollo Decodable.
  2. Un case dell’enum CodingKeys, cioè la chiave da cui prendere il valore.

Ed, infine, restituisce un oggetto del tipo passato al primo parametro. In più il metodo può sollevare eccezioni (quindi va marcato con un try).

Quindi, nel caso dell’inizializzazione della proprietà name, prenderemo il valore dal container scrivendo:

self.name = try container.decode(String.self, forKey: .name)

Il type è String dato che il valore associato alla chiave name è per l’appunto una stringa.

Per quanto riguarda la chiave id, questa nel JSON è una String. Quindi, prima la prenderemo nel suo tipo originale, poi la convertiremo in Int ed, infine, la assegneremo alla proprietà id:

let string_id = try container.decode(String.self, forKey: .id)
let id = Int(string_id) ?? 0
      
self.id = id

Gestisci il casting in Int come preferisci (per semplicità io ho usato il nil coalescing, ma puoi tranquillamente usare un guard o if-let. L’importante è inizializzare la proprietà id con un valore dato che non è opzionale).

Così facendo sarai in grado di gestire tipi diversi tra JSON ed i tuoi model senza uscir pazzo e continuando a sfruttare il Codable.

Infine, il codice completo, dovrebbe essere il seguente:

import UIKit

let string = """
{
    "id": "1234",
    "name": "Giuseppe Sapienza"
}
"""

let data = string.data(using: .utf8)!


struct User: Codable {
    var id: Int
    var name: String
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: User.CodingKeys.self)
        self.name = try container.decode(String.self, forKey: .name)
        
        let string_id = try container.decode(String.self, forKey: .id)
        let id = Int(string_id) ?? 0
        
        self.id = id
    }
}

do {
    
    let user = try JSONDecoder().decode(User.self, from: data)
    print(user.id, user.name)
    
} catch let e {
    print(e)
}

Container.decodeIfPresent

Il nome non trae in inganno. Utilizzando gli opzionali siamo riusciti a gestire il caso in cui una chiave è assente o il suo valore è null.

Il metodo container.decodeIfPresent prova a decodificare una chiave solo se è presente e restituisce nil in caso contrario.

Questo metodo è utilissimo quando stai utilizzando l’init ed hai bisogno di:

  • gestire le tue proprietà opzionali
  • provare a decodificare chiavi che non sai se sono o meno presenti e vuoi continuare ad usare proprietà non opzionali (quindi assegnandogli valori di default).

Vediamo l’ultimo caso.

Hai un JSON che contiene una proprietà imageURL che può essere null o meno. Nel caso in cui sia null, invece di usare proprietà opzionali vuoi passare un valore di default (un’immagine di default):

let string = """
{
    "title": "Encode e Decode di JSON con il protocollo Codable",
    "imageURL": null
}
"""
let data = string.data(using: .utf8)!

E la nostra struct sarà qualcosa del genere:

struct Post: Codable {
    var title: String
    var imageURL: URL
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: Post.CodingKeys.self)
        
        self.title = try container.decode(String.self, forKey: .title)
        
        let url = try container.decodeIfPresent(URL.self, forKey: .imageURL)
        self.imageURL = url ?? URL.init(string: "https://sito.it/image.jpg")!
    }
}

Anche se la proprietà imageURL doveva essere impostata come opzionale, grazie al decodeIfPresent, siamo riusciti a mantenere la proprietà “normale”.

L’unica eccezione è che, in assenza di valore, avrà un URL di default (che era quello che volevamo).

Conclusione

Il linguaggio Swift, versione dopo versione, sta facendo enormi passi in avanti.

Con i protocolli Encodable e Decodable abbiamo, finalmente, uno strumento versatile e potente che ci permette di interagire con i nostri web services.

Per il resto, spero di averti dato tutte le nozioni principali legate al Codable ed al suo utilizzo.

Man mano che incontrerò nuove sfaccettature e casi d’uso le ripoterò qui dentro.

Per qualsiasi dubbio o problema, non esitare a scrivermi!

Buona Programmazione! 😎

Xcode 10: Cosa c’è di nuovo?

xcode-10-le-novit-del-wwdc-2018

Il WWDC 2018 è giunto a termine. Come ormai consuetudine il keynote è più un momento di puro intrattenimento che una vera e propria developers conference. Qui vengono mostrate le novità dei nuovi OS e si lasciano i dietro le quinte di questi update a tutte le piccole sessioni d’approfondimento che si protraggono nei giorni successivi.

Noi di una cosa possiamo cominciare già a parlarne, ovvero del nostro compagno quotidiano: Xcode 10 (puoi scaricarlo in beta dal seguente link).

dark mode Xcode 10

La domanda che ci facciamo tutti, esploderanno i miei progetti con Xcode 10?

L’anno scorso è stato un vero e proprio incubo con la beta di Xcode 9. Se ti ricordi, sembrava più il frutto di una sbronzata e di un upload partito per sbaglio che di una vera e propria beta pubblica. Tantè che in molti siamo stati a lamentarci dei continui crash inaspettati, della lentezza e dei vari dettagli non curati (che si sono protratti anche nelle versioni definitive).

Con Xcode 10 sembra che Apple abbia recepito il messaggio. A primo acchitto Xcode 10 (beta) sembra mettere nel cassetto molti dei problemi che avevamo incontrato nelle puntate precedenti.

Quindi, si, puoi scaricare senza problemi in nuovo Xcode 10 (con le dovute accortezze! è sempre in beta).

Ma veniamo subito al nocciolo della questione, cosa porta di nuovo Xcode 10?

Xcode 10 è il frutto di un processo lento ma continuo che porta l’IDE di mamma Apple da un programmino di seconda categoria ad un vero e proprio caposaldo dello sviluppo. Vedrai che non ci sono novità rivoluzionarie ma tante piccole cose che messe insieme cominciano a fare la differenza.

P.S. Passa di tanto in tanto da qui o seguici su Slack o Facebook perché aggiornerò di continuo questo articolo. Nel frattempo ti avviso che aggiornerò il corso gratuito sul linguaggio Swift, il corso iOS Top Down e Auto Layout Ground per supportare le ultime novità.

Adesso veniamo ad Xcode 10.

DarkMode

Penso non ci sia niente d’aggiungere se non quello che vedi nello screen che ho messo sopra. Con Xcode 10, grazie a macOS Mojave, avremo una Dark Mode all’interno dell’Editor.

Editor migliorato

Apple ha un amore maniacale per il testo ed i font. Con Xcode 10 noterai un enorme salto di qualità all’interno dell’editor. Il testo ti apparirà più morbido e con delle tonalità di colori differenti (per supportare la Dark Mode). 

La prima novità riguarda la velocità dell’editor. A detta di apple, ma si nota già con progetti corposi, il tempo di apertura è stato aumentato di 2x volte. 

È stato introdotto il folding, ovvero la capacità di accorpare righe per leggere il codice più velocemente:

xcode-10-folding

Con il Source Control abilitato, all’interno dell’Editor, potrai vedere delle barrette colorate accanto al numero di linee che ti identificheranno lo status del codice in relazione a quello del tuo git. 

Per esempio se quella porzione di codice che hai modificato entra in conflitto con quella del tuo team (rossa/arancione), oppure se di quella parte devi ancora fare il push…

In più dal click alla barra potrai eseguire diverse operazioni:

source-control-editor-xcode-10

editor-xcode-10-discard

Hanno migliorato il pannello delle Actions alla selezione del codice. Nel particolare la funzione Jump To Definition adesso aprirà un menù contestuale che mostrerà tutte le definizioni e la loro posizione di quella particolare funzione/metodo, proprietà, classe etc selezionato.

In un esempio ho creato una classe MyLogger in cui ho definito un metodo debug() ed una subclass MySpecialLogger in cui ho eseguito un override del debug(). Nella fase di utilizzo il Jump To Definition mi permetterà di saltare al metodo genitore o al figlio:

xcode-10-jump-to-definition

Infine, un’ultima novità dell’editor di Xcode 10 degna di nota è la possibilità di editare diversi componenti del codice in maniera simultanea.

Questa nuova funzionalità si chiama Multi-Cursor Editing e si attiva:

  1. Tenendo premuti ⌃+⇧+Click e cliccando sugli elementi
  2. o con la tastiera premendo ⌃+⇧+Up⌃+⇧+Down 
  3. oppure ancora premendo ⌥+Click+Drag e trascinando il mouse

Quello che potrai fare sarà la seguente:

quick-editing-xcode-10

Il tempo di prendere dimestichezza con questo strumento e vedrai che non ne potrai fare più a meno!

Source Control

Ora il Source Control di Xcode 10 supporta Bitbucket Cloud e Server, GitLab.com e GitLab self-hosted (oltre al già presente GitHub).

Xcode genera in maniera automatica l’SSH key e fa l’upload in maniera automatica ed il navigator del Source Control è stato leggermente migliorato (per esempio adesso ti basterà fare un doppio click su un commit e non si aprirà più una finestra a parte).

E attenzione, adesso Xcode permette il rebase delle tue modifiche quando esegui in pull dell’ultima versione dalla tua repo.

Build System

Xcode 10 porta con se un nuovo build system che aumenta le performance in maniera drastica. Nel caso in cui volessi utilizzare ancora il Legacy build system puoi riattivarla andando su File/Project Settings.

xcode-10-build-system

Questo nuovo build system (che già era disponibile in preview in Xcode 9) è completamente riscritto con il linguaggio Swift. Ti assicuro che noterai immediatamente la differenza di velocità non appena farai partire i Build dei progetti (e sopratutto nei successivi).

Testing

Le maggiori novità di Xcode 10 le si hanno in ambito di XCTest ed UITest. Xcode 10, per la prima volta, supporta il testing parallelo (in macOS, tvOS e ovviamente iOS).

Per implementare il testing parallelo devi andare su Product/Scheme/Edit-Scheme. Da lì selezionare Test e cliccare sul bottone Options. Si aprirà un menù in cui potrai spuntare Execute in parallel on Simulator:

testing-parallelo-xcode-10

Dalle stesse opzioni potrai anche sattivare l’esecuzione randomica dei test.

Una volta abilitato il parallelismo, avviando i Test verrano avviate N istanze del simulatore in base al grado di parallelismo che può erogare la tua CPU.

Conclusione

Di default Xcode 10 beta include il linguaggio Swift 4.2 e le beta SDKs per iOS 12, watchOS 5, tvOS 12, e macOS Mojave.

Aggiornerò l’articolo di giorno in giorno in modo tale da poterti lasciare con un resoconto completo di tutte le novità.

Se ti fa piacere darmi una mano ed hai notato qualche novità che manca all’articolo, scrivimela pure nei commenti in modo tale da poterla aggiungere.

Buona programmazione!

Le novità del Linguaggio Swift 4.0. Tanti buoni motivi per aggiornarsi!

le-novità-del-linguaggio-swift-4.0

Giuro, non l’avrei mai detto! Questa volta, non vedo l’ora che rilascino la versione 4.0 del linguaggio Swift!

Ogni aggiornamento del linguaggio Swift è stato un vero e proprio caos generale. Tra sintassi che cambiavano in continuazione ad aggiornamenti strutturali delle classi/strutture Foundation. Tutti avevamo il timore di aggiornare i nostri progetti all’ultima versione.

La 4.0, finalmente, da tanti buoni motivi per cambiare. Dall’introduzione della serializzazione degli oggetti in JSON e viceversa, alle String multiline fino all’introduzione del key-path. 

Tante piccole modifiche che nel complesso cominciano a portare il linguaggio Swift a competere con linguaggi Enterprise come C#, C++ e Java!

Quindi, sei pronto a scoprire le novità del linguaggio Swift 4.0?
Allora cominciamo!

Download di Swift 4.0

Essendo ancora in beta non lo troverai di default su Xcode, devi scaricarlo manualmente.

Per farlo, comincia entrando nel sito ufficiale di swift.org, poi entra nella sezione Download/Snapshots. Scorri la pagina fin quando non troverai “Swift 4.0 Development“. Da lì, clicca su “Xcode” per scaricare la versione compatibile con il tuo IDE:

swift-4.0-le-novità

Una volta scaricato ed installato, apri Xcode. Spostati sul menu in alto e seleziona Xcode/Toolchains e seleziona la nuova versione di Swift:

utilizzare-swift-4.0-con-xcode-beta

Se avevi Xcode aperto durante il processo di installazione, chiudilo e riaprilo. Infine crea un nuovo Playground in modo da poter provare le novità.

JSON Encode e Decode

Partiamo dalla modifica in assoluto più interessante.

Per chi deve interfacciare la sua applicazione con una web app o BAAS come Firebase che restituiscono file JSON, sa benissimo quanto sia noioso e pieno di insidie trasformare il dato restituito dalla chiamata in un modello malleabile localmente.

Tutto parte dalla definizione del Model dei tuoi dati. Immaginiamo d’avere un’app che scaricare i blog post di un sito. Una volta create le strutture ti basterà fargli ereditare il protocollo Codable:

import Foundation

struct Author: Codable {
    var name: String
    var twitter: String
    var bio: String
}

struct Post: Codable {
    var title: String
    var content: String
    
    var author: Author
}

A livello di utilizzo non cambia assolutamente nulla. La creazione degli oggetti avviene sempre allo stesso modo, cioè invocando l’init:

let author = Author.init(name: "Giuseppe Sapienza", twitter: "@peppesapienza", bio: "iOS Developers")

let post = Post.init(title: "Le novità di Swift 4.0", content: "blabla", author: author)

JSONEncoder. Da Model a JSON

Vediamo adesso come trasformare velocemente questo oggetto post in un JSON.

Tutto parte della creazione del JSONEncoder:

let encoder = JSONEncoder.init()

Il JSONEncoder ti mette a disposizione il metodo encode(value: Codable) il quale accetta un oggetto la cui classe/struct implementi il Codable. Questo metodo è di tipo throws quindi va gestito con un do-try-catch:

do {
    
    try encoder.encode(post)
    
} catch let error {
    print("errore nella codifica dei dati", error)
}

L’encode restituisce un Data il quale all’interno contiene il JSON serializzato. A questo punto ti basta inviarlo tramite una URLSession o semplicemente stamparlo in console:

import Foundation

struct Author: Codable {
    var name: String
    var twitter: String
    var bio: String
}

struct Post: Codable {
    var title: String
    var content: String
    
    var author: Author
}


let author = Author.init(name: "Giuseppe Sapienza", twitter: "@peppesapienza", bio: "iOS Developers")

let post = Post.init(title: "Le novità di Swift 4.0", content: "blabla", author: author)


let encoder = JSONEncoder.init()

do {
    
    let data = try encoder.encode(post)
    
    if let json = String.init(data: data, encoding: .utf8) {
        print(json)
    }
    
} catch let error {
    print("errore nella codifica dei dati", error)
}

Che stamperà:

{"title":"Le novità di Swift 4.0","content":"blabla","author":{"name":"Giuseppe Sapienza","twitter":"@peppesapienza","bio":"iOS Developers"}}

Semplice, facile ed indolore 😎

JSONDecoder. Da JSON a Model

Ipotizziamo di aver scaricato un blog post tramite una chiamata che ci restituisce il data contenente il JSON. A questo punto dobbiamo trasformarlo nel modello che lo rappresenta.

let jsonString = """
{"title":"Le novità di Swift 4.0","content":"blabla","author":{"name":"Giuseppe Sapienza","twitter":"@peppesapienza","bio":"iOS Developers"}}
"""
let data = jsonString.data(using: .utf8) // esempio di un data restituito da una chiamata get

In nostro aiuto arriva l’oggetto JSONDecoderQuesto ti mette a disposizione il metodo decoder che accetta in ingresso il tipo di dato che fa da modello ed i dati che vuoi convertire in quel modello. La funzione restituisce un oggetto contenente i dati del json:

let decoder = JSONDecoder.init()

do {
    
    let post = try decoder.decode(Post.self, from: data!)
    print(post.title)
    print(post.author.name)
    
} catch let error {
    print("Errore nella conversione del json", error)
}

Anche qui, niente più guard, if let e compagnia bella!

L’unica regola da rispettare è quella che le proprietà della classe/struttura si chiamino allo stesso modo delle chiavi del json. Se ci sono dizionari annidati, questi devono essere rappresenti da una struttura/classe interna (come il caso dell’author).

String Multiline

Finalmente potremo scrivere stringhe senza l’aggiunta dei tag “\n” e compagnia. Ti basterà inserire tre virgolette “”” ad inizio e fine per attivare la possibilità di scrivere testo libero e su più linee.

L’unica regola è che dopo le virgolette si vada a capo in modo che il testo sia su una linea separata:

let someText = """
Questo è un testo multilinea
Io verrà stampato su una nuova linea, 
Puoi anche "aggiungere virgolette" e tutto fila liscio
Si possono aggiungere anche le emojii 😂😂😂 senza problemi
"""

print(someText)

Swift metterà automaticamente i tag per la corretta visualizzazione. Questo è quello che vedrà l’app:

"Questo è un testo multilinea\nIo verrà stampato su una nuova linea, \nPuoi anche "aggiungere virgolette" e tutto fila liscio\nSi possono aggiungere anche le emojii 😂😂😂 senza problemi"

Questo è quello che vedrai tu:

stringhe multilinee swift 4

 

String come Collection di Character

Non si sa per quale oscuro motivo, ma da Swift 2 in poi hanno eliminato la possibilità di iterare una stringa. Fortunatamente in Swift 4 hanno fatto un revert della struct:

let stringa = "ciao xCoders"

stringa.count // in swift 3 e prec era: stringa.characters.count

for c in stringa { // in swift 3 e prec: for c in stringa.characters
    print(c)
}

SwapAt su una MutableCollection

Adesso è possibile invertire due elementi in una MutableCollection  semplicemente definendo gli indici:

var nomi = ["giuseppe", "luca", "delia", "matteo"]
nomi.swapAt(0, 1) // ["luca", "giuseppe", "delia", "matteo"]

Range con un solo estremo

Adesso si possono estrarre gli elementi da una Collection definendo solamente uno degli estremi di un Range:

var nomi = ["giuseppe", "luca", "delia", "matteo", "emanuele", "sebastiano"]

nomi[..<2] // ["giuseppe", "luca"]
nomi[3...] // ["matteo", "emanuele", "sebastiano"]
nomi[2...4] // ["delia", "matteo", "emanuele"]

Utile nel caso in cui si volessero pescare, per esempio, gli ultimi elementi inseriti o i primi o gli intermedi di una collezione.

Modifiche ai Dizionari

Adesso puoi creare un dizionario partendo da un array:

var nomi = ["giuseppe", "luca", "delia", "matteo", "emanuele", "sebastiano"]

let dizionarioNomi = Dictionary.init(uniqueKeysWithValues: zip(1..., nomi))
// [5: "emanuele", 6: "sebastiano", 2: "luca", 3: "delia", 1: "giuseppe", 4: "matteo"]

La funzione zip prenderà ogni elemento e lo assocerà ad un numero che parte da 1 e viene incrementato fino all’indice N rappresentato dell’ultimo elemento della collezione. Infine la costruzione del dizionario lo trasformerà come tale.


Durante la fase di lettura,
cioè accesso ad una chiave, puoi definire un valore di default nel caso la chiave a cui stai provando ad accedere non esista:

dizionarioNomi[10, default: "Questo indice non esiste"]

Infine, puoi raggruppare i valori di un array in un dizionario definendo un tuo particolare filtro. Per esempio, quello di utilizzare come chiave la prima lettera dell’elemento dell’array:

var nomi = ["giuseppe", "giovanni", "delia", "enzo", "emanuele", "matteo"]

let dizionario = Dictionary.init(grouping: nomi, by: { $0.first! })

// ["e": ["enzo", "emanuele"], "g": ["giuseppe", "giovanni"], "d": ["delia"], "m": ["matteo"]]

Considerazioni

Ora che hai visto un po’ di modifiche, scommettiamo che aggiornerai anche tu al primo colpo? 💪

Dopo anni di sofferenze, adesso si comincia a vedere la luce! Ad ogni modo, se volessi tenere traccia degli aggiornamenti futuri, puoi farlo da qui. Se ne dovessero uscire altri interessanti, li aggiungerò a questo post.

Alcuni esempi di questo articolo sono stati presi dal tutorial di mindorks.com.

Buona Programmazione!

Changelog

  • 30/05/2017 – Prima versione del tutorial.

Principio di Singola Responsabilità applicato al linguaggio Swift

prinicpio-di-singola-responsabilit

A fare più cose contemporaneamente c’è il rischio di confondersi. Se questa sembra una cosa assodata nella vita reale, nella programmazione molti tendono a dimenticarsene.

Il principio di Singola Responsabilità stabilisce che una classe deve essere responsabile di una singola e sola funzionalità dell’applicazione. Questa classe, inoltre, deve incapsularne tutto il comportamento.

Secondo l’autore di questo principio, lo zio Bob (Robert C. Martin), si definisce Responsabilità il motivo per cui una classe potrebbe cambiare. Di conseguenza, una classe o metodo, per rispettare il principio, dovrebbe avere un solo motivo per cambiare.principio di singola responsabilità

Cosa vuol dire cambiare? e come si applica il principio di Singola Responsabilità?
Vediamo il principio di Singola Responsabilità applicato al linguaggio Swift!

Prima di continuare, se non lo hai ancora fatto, potrebbe esserti d’aiuto e propedeutica la lettura del tutorial sulla legge di Demetra.

Esempio

Ipotizziamo che una società ti commissioni la realizzazione di un’applicazione che ha un e-commerce accessibile da più paesi.

I prodotti da visualizzare sono composti, per semplicità di spiegazione, da due campi: il nome ed il prezzo. Quest’ultimo, cioè il prezzo, è scorporato dalle tasse e, dato che l’app viene utilizzata da più parti del mondo, deve poter essere visualizzato con la VAT (cioè l’IVA) del paese dell’utente.

Uno sviluppatore alle prime armi con la OOP (Programmazione Orientata agli Oggetti), nel 99% dei casi, creerà qualcosa di simile a questo:

// NESSUNA APPLICAZIONE DEL PRINCIPIO DI SINGOLA RESPONSABILITÀ
// NON IMITARE - NUOCE GRAVEMENTE ALLA SALUTE 
class Product {
    var name: String
    var price: Double
    
    init(name: String, price: Double) {
        self.name = name
        self.price = price
    }
    
    func priceWithVAT(country: String) -> Double {
        var vat: Double = 0.0
        switch country {
        case "it":
            vat = 0.22
            
        case "de":
            vat = 0.19
            
        case "fr":
            vat = 0.20
            
        default:
            return 0
        }
        
        return price + (price * vat)
    }
}

var iphone7 = Product.init(name: "iPhone 7", price: 699.99) 
iphone7.priceWithVAT(country: "it")

Peppe, ma sei sicuro che ci siano dei problemi? perché io non vedo niente di sbagliato in questa classe!

Esatto, l’impiccio sta proprio nel non vederne i problemi 😎

Prima applicazione del principio

Comincia a guardare le classi dal punto di vista della Singola Responsabilità e poniti la domanda: per quali motivi dovrei poter cambiare il comportamento (codice della classe)? 

Ci sono due motivi per cui potresti mettere mano alla classe:

  1. In primo luogo, il contenuto del Report può cambiare. Cioè capiterà, in futuro, di dover aggiungere nuove proprietà o di dover cambiare la struttura dei dati
  2. In secondo luogo, il metodo del calcolo del VAT può cambiare in contenuto e forma di calcolo

Una volta trovati due o più motivi non resta altro che domandarsi: come suddivido i motivi in classi a se stanti?

Il calcolo del VAT è qualcosa che ha a che fare con il prezzo. Di conseguenza ne conviene che il prezzo potrebbe vivere in una classe a se stante che, oltre ad avere il compito di gestire il valore al netto di tasse, può restituirne uno nuovo con l’aggiunta della tassa:

class Price {
    var base: Double
    
    init(base: Double) {
        self.base = base
    }
    
    func withVAT(country: String) -> Double {
        var vat: Double = 0.0
        switch country {
        case "it":
            vat = 0.22
            
        case "de":
            vat = 0.19
            
        case "fr":
            vat = 0.20
            
        default:
            return 0
        }
        
        return base + (base * vat)
    }
}

class Product {
    var name: String
    var price: Price
    
    init(name: String, price: Price) {
        self.name = name
        self.price = price
    }
}

var iphone7 = ProductA.init(name: "iPhone 7", price: Price.init(base: 669.99))
iphone7.price.withVAT(country: "it")

Decisamente una struttura più snella e facile da comprendere. Adesso, tutta la logica per la gestione del prezzo è relegata e rinchiusa all’interno della classe Price. 

La classe Product è diventata indipendente dai cambiamenti interni della classe Price.

Non noti ancora i benefici?

Immagina la classe Product all’interno di un vero progetto. Senza l’applicazione del principio di Singola Responsabilità ti ritroverai sicuramente ad avere decine di metodi e proprietà all’interno della classe Product.

Questo significa che, in un ipotetico caso di controllo e/o ricerca dell’errore, perderai del tempo solamente a cercare visivamente, ed all’interno della classe, il metodo/proprietà coinvolto. In più, il fatto di aver mischiato responsabilità diverse ti porterà a chiederti: É l’intera classe che ha problemi? i metodi vanno in conflitto tra loro?

Con l’utilizzo del principio di Singola Responsabilità anche i problemi e gli errori si responsabilizzano.

Per esempio, se si dovesse verificare un errore sul calcolo del prezzo sai che dovrai andarlo a ricerca all’interno della classe che lo controlla e non più all’interno di una classe più vasta e che contiene responsabilità differenti.

Seconda applicazione del principio

Ma, torniamo a noi!

Se sei stato attento, adesso è la classe Price che ha più responsabilità differenti. O meglio, il suo metodo withVAT ha più di un motivo per cambiare. Il primo riguarda un problema di contenuto, cioè il calcolo della VAT (un domani potrebbe cambiare la formula). Il secondo è relativo alla struttura utilizzata per generare il valore di IVA del paese in questione.

Lo switch sulla stringa porta con se diversi problemi. Il più importante è quello che bisogna inserire la stringa in modo che sia uguale ad uno dei case presenti. Cioè se io dovessi scrivere “IT” il codice non funzionerà perché si aspetta un “it”.

Potresti, quindi, rendere la generazione del VAT dipendente da un enum (dato che si tratterebbe solo di un elenco di valori):

enum Country {
    case it, de, fr
    
    var vat: Double {
        get {
            switch self {
            case .it:
                return 0.22
                
            case .de:
                return 0.29
                
            case .fr:
                return 0.20
            }
        }
    }
}

var italy = Country.it.vat

Quello che viene fuori è una struttura di classi completamente indipendente l’una dall’altro e, cosa più importante, ognuna di questa ha una singola responsabilità cioè si occupa di una e solamente una parte dell’applicazione:

// APPLICAZIONE CORRETTA DEL PRINCIPIO DI SINGOLA RESPONSABILITÀ

enum Country {
    case it, de, fr
    
    var vat: Double {
        get {
            switch self {
            case .it:
                return 0.22
                
            case .de:
                return 0.29
                
            case .fr:
                return 0.20
            }
        }
    }
}

class Price {
    
    var base: Double
    
    init(base: Double) {
        self.base = base
    }
    
    func with(VAT country: Country) -> Double {
        return base + (base * country.vat)
    }
    
}

class Product {
    
    var name: String
    var price: Price
    
    
    init(name: String, price: Price) {
        self.name = name
        self.price = price
    }
    
}

var iphone7Plus = Product.init(name: "iPhone 7+", price: Price.init(base: 699.99))
iphone7Plus.price.with(VAT: .it)

Ci sarebbe altre modifiche da fare (ma che non riguardano il principio) e per evitare di appesantire il tutorial eviterò di fare.

Perché usarlo?

Arrivati a questo punto generalmente in testa appare una sola e singola domanda: perché?

  1. Perché dovrei creare tre sovrastrutture quando con una potevo risolvere tutti i problemi?
  2. Perché devo utilizzarlo se tanto alla fine nel mio piccolo progetto le cose vanno bene così come sono?

Comincio col riprendere una frase detta nel tutorial sulla legge di Demetra (ti consiglio di leggerlo): Non perché puoi farlo significa che devi farlo. 

Però, in effetti hai ragione, io non ti biasimo. La scelta spetta solamente a te, alla fine della storia, l’applicazione funzionerà lo stesso.

Ma ha senso procedere senza regole?

Lasciamo stare per un momento la storia della bella scrittura del codice, della pulizia e della corretta applicazione.

Molti tirano in ballo la carte del tempo: “Perdo un sacco di tempo a seguire tutte queste regole“, “Devo sbrigarmi a rilasciare l’app, chissene**** dei pattern“.

Tra me e me sorrido quando mi rispondono così (e non far finta di non sapere di che sto parlando, probabilmente rientri anche tu nella categoria 😂 ).

É vero si perde del tempo ad acquisire una piena padronanza di queste regole. Ma questo tempo che perdi, non lo riacquisterai tutto più avanti?

Immagina di dover cucinare una frittata.

Se non sapessi che servono le uova, una padella e che devi versarle dentro, perderesti un sacco di tempo a cercare come si cuociono due uova. Invece, quel tempo non lo perdi perché sai esattamente come e cosa devi fare.

Molti continuano a pensare che le applicazioni sono sempre diverse l’una dall’altra. Ma invece no. Nella loro struttura di base sono tutte uguali, sono tutte delle frittate.

Se sai cosa sono sai anche come controllarle. Se so che ho delle strutture che rispecchiano una regola so sempre dove andare a cercare il problema. Se invece ho strutture casuali la mia ricerca sarà sempre casuale. Potrò avere la fortuna di trovare il problema al primo tentativo come potrei non trovare l’errore se non dopo ore ed ore di ricerca.

Va bene. mi hai convinto, al primo progetto importante seguirò questo consiglio.

Proprio qui casca l’asino.

Scrivi correttamente solo quando devi scrivere email importanti? Baci bene la tua lei o il tuo lui solo al primo appuntamento?

Se non ti eserciterai con i progetti piccoli, al primo progetto enorme, ti ritroverai sottoterra nel giro di poche ore. Queste regole non si apprendono leggendole, si apprendono con la pratica.

Considerazioni

Il principio di Singola Responsabilità o abbreviato in SRP fa parte di una famiglia di principi che viene chiamata SOLIDDove la S sta proprio ad indicare Responsibility.

Questi principi formano la base per la corretta interpretazione ed utilizzo di base della programmazione orientata agli oggetti. La comprensione di dei principi SOLID è anche il punto d’ingresso per lo step successivo che sono i pattern di programmazione.

Pian piano conto di poterli descrivere tutti!

A questo punto, se non lo hai ancora fatto, ti consiglio la lettura sulla legge di Demetra. Un’altra piccola chicca che ti aiuterà nella scrittura del codice.

Buona Programmazione!

Le differenze tra struct e class del linguaggio Swift

Le differenze tra struct e class del linguaggio Swift

Una delle tante domande che scatta non appena entrati in contatto con la programmazione ad oggetti è: Quali sono le differenze tra struct e class del linguaggio Swift?

La confusione nasce dal fatto che sia le classi che le strutture hanno caratteristiche simili che non permettono, ad un programmatore alle prime armi, di capirne le differenze.

Infatti, per il linguaggio Swift, sia le classi che le strutture vengono considerati oggetti ed entrambe:

  1. Possono definire delle proprietà per conservare valori
  2. Possono avere dei metodi che forniscono funzionalità o che lavorano con altri oggetti
  3. Sono estensibili tramite le extension
  4. Possono implementare dei protocol 
  5. Forniscono gli init o costruttori con il quale è possibile creare gli oggetti
  6. Possono avere dei subscript per accedere ai valori delle proprietà
class Casa {
    var nome: String
    var indirizzo: String


    init(nome: String, indirizzo: String) {
        self.nome = nome
        self.indirizzo = indirizzo
    }

    func stampa() {
        print(nome, indirizzo)
    }

}

struct Casa {
    var nome: String
    var indirizzo: String

    func stampa() {
        print(nome, indirizzo)
    }

}

Allora, cos’è che differenzia una struttura da una classe?
Scopriamolo insieme!

Classi: Ereditarietà, casting e deinit

Le classi sono uno strumento potentissimo. Danno vita a quello che il mondo informatico chiama pattern di programmazione orientata agli oggetti.

Una classe (come anche una struct) è la maschera di un insieme di proprietà e metodi che sono accumunate da uno scopo. Una classe permette di astrarre la complessità di un problema mascherando, all’interno di essa, tutte le logiche ed informazioni.

Le classi però vanno oltre questo concetto aggiungendo la possibilità di ereditare gli attributi e metodi da una classe genitore. Permettendoti di estendere e diversificare un problema con una precisione che le strutture, purtroppo, non permettono.

class Animale {
    var nome: String!
}

class Mucca: Animale {}
class Maiale: Animale {}

var fattoria: [Animale] = [Mucca(), Mucca(), Maiale()]

Se questa sintassi ti è nuova, forse dovresti andare a leggere la lezione gratuita sull’ereditarietà delle classi.

In questo modo, l’ereditarietà, ti permette di lavorare su oggetti logicamente simili e praticamente accumunati dall’essere figli di una classe genitore. Cosa che comporta il poter utilizzare i metodi ed attributi del genitore e tutte le operazioni di casting:

var fattoria: [Animale] = [Mucca(), Mucca(), Maiale()]

for animale in fattoria {
    if let mucca = animale as? Mucca  {
        print(mucca.nome, "è una mucca")
    }

    if let maiale = animale as? Maiale {
        print(maiale.nome, "è un maiale")
    }
}

Infine, le classi, permettono di definire un metodo di deinit che funziona esattamente all’inverso di un init. Viene invocato durante il processo di distruzione dell’oggetto.

Non entro nei dettagli perché si aprirebbe un argomento a parte che si chiama Automatic Reference Counting ma per capire un po’ di che si tratta, il deinit viene chiamato quando l’ultimo riferimento ad un oggetto viene passato a nil:

class UnaClasse {
    init() {
        // costruzione dell'oggetto
    }

    deinit {
       // distruzione dell'oggetto
    }
   
}class UnaClasse {
    init() {
        // costruzione dell'oggetto
        print("creo l'oggetto")
    }
    
    deinit {
        // distruzione dell'oggetto
        print("distruggo l'oggetto")
    }
    
}

var unaVar: UnaClasse? = UnaClasse.init()

unaVar = nil // chiamata al deinit

Il perché della sua esistenza è una diretta conseguenza dei prossimi paragrafi. Ad ogni modo ne parlerò in un tutorial dedicato all’argomento.

Il concetto di Puntatore o Riferimento

Figo, bello e bellissimo! Così per come ho presentato le classi, solo un pazzo si azzarderebbe ad utilizzare le struct perché risultano essere decisamente più limitate.

Allora finiamo qua?

Per arrivare al punto principale, devo farti scendere nelle viscere della programmazione.

All’interno del corso gratuito, precisamente qui, ho accennato a come vengono memorizzate le informazioni all’interno della memoria di un dispositivo.

Per farla breve, la memoria dinamica devi immaginarla come ad una tabella con due colonne. La colonna di sinistra rappresenta gli indirizzi di memoria, più o meno come un’indirizzo di una casa in una via, e la colonna di destra rappresenta il contenuto cioè chi abita in quell’indirizzo.

indirizzi di memoria

Questo vuol dire che quando vado a creare una nuova variabile:

var unaVar: Int = 45

Il sistema prenderà un indirizzo libero, per esempio il n.1, e metterà al suo interno il valore 45. Quando andrai a leggere la variabile unaVar, il sistema saprà recuperare il valore 45 perché conosce l’indirizzo di memoria in cui è stato conservato.

Quindi, le variabili (come le costanti), non sono altro che riferimenti (o puntatori) a specifici indirizzi di memoria.

Value Type

Le strutture hanno la proprietà d’essere di tipo Value Type.

Per capire il significato voglio farti un esempio:

struct UnaStruct {
    var proprietà: Int
}

Ipotizziamo di creare un oggetto di questa struttura, di modificarlo e di passarlo ad un’altra variabile:

var a = UnaStruct()
a.proprietà = 10

var b = a

b.proprietà = 20

print(a.proprietà) // stampa 10
print(b.proprietà) // stampa 20

Il passaggio di un oggetto, nato da una struct, da a verso b implica la creazione di un nuovo oggetto copia dell’oggetto passato.

Mi spiego meglio.

Ad a ho dato il valore 10 alla sua proprietà. Poi ho creato una var b a cui ho passato l’oggetto a. In questo passaggio il sistema ha creato un nuovo oggetto copia di a (quindi avrà lo stesso valore 10) che ha poi passato alla nuova var.

Essendo due oggetti diversi, la modifica di b non influirà sull’oggetto a perché trattasi di due oggetti diversi. Questo significa che, a livello di memoria, la variabile a e la variabile b puntano a due indirizzi differenti.

I tipi di dato fondamentali: String, Int, Double, Float, Character ecc sono strutture e quindi sono di tipo Value Type. Puoi accorgertene dal simbolo S accanto all’assistente di scrittura.

var a: Int = 5

var b = a // Creazione di un oggetto Int con valore 5, passato alla var b

b = 10

print(a) // 5
print(b) // 10

Solo per correttezza di informazioni. Non è sempre vero che una struttura viene immediatamente copiata al momento del passaggio. Per evitare problemi di sovraccarico, la copiatura in un nuovo riferimento viene effettuato solo quando viene modificato un valore dell’oggetto.

Reference Type

Le classi hanno la proprietà d’essere di tipo Reference Type.

Ipotizziamo d’avere la seguente classe e di eseguire un passaggio di un oggetto di questo tipo da una var a verso una var b:

class Utente {
var nome: String = ""
}

var a = Utente()
a.nome = "Giuseppe"

var b = a

b.nome = "Luca"

print(a.nome) // Luca
print(b.nome) // Luca

La modifica di b.nome ha comportato pure la modifica di a.nome. Più in generale, la modifica dell’oggetto conservato in b ha avuto come conseguenza anche la modifica dell’oggetto conservato in a.

Come?

Reference Type significa che il passaggio di un oggetto, nato da una classe, verso una var/let implica il solo passaggio del riferimento a quell’oggetto.

Questo vuol dire che, l’oggetto contenuto in b e quello contenuto in a, sono lo stesso identico oggetto. A livello di memoria, la var a e la var b puntano esattamente allo stesso identico indirizzo di memoria.

Conseguenze

Vediamo alcuni problemi dall’utilizzo di una o dell’altra soluzione.

No alle struct come contenitori di grandi informazioni

Il passaggio di strutture dovresti aver capito che crea delle copie quando vengono passate da un punto all’altro. Questo, però, non significa che le copie precedenti vengano sempre distrutte automaticamente.

Di conseguenza, con le struct, potresti andar in contro a problemi di sovraccarico della memoria o di rallentamento in caso di strutture molto grandi.

Per questo motivo, se hai notato,  gli oggetti del framework Foundation per la gestione di file, che potrebbero avere grandi dimensioni, vengono gestiti tramite classi. In questo modo, durante il passaggio da un punto all’altro dell’applicazione, verrà sempre passato il riferimento.

Attenzione alle classi ed al reference counting

All’interno del nostro dispositivo esiste un modulo che si chiama ARC o Automatic Reference Counting che gestisce e tiene traccia di tutti i passaggi di riferimento di un oggetto, nato da una classe, ad un altro oggetto.

In pratica è un contatore che viene incrementato ad ogni passaggio:

class Classe {
    deinit { print("oggetto distrutto") }
}

var a: Classe? = Classe() // ARC: 1
var b: Classe? = a // ARC: 2

Dato che viene passato il riferimento, sia che sono lo stesso oggetto. Questo ci ti può portare a credere che la distruzione della var b comporti anche la distruzione dell’oggetto a lui collegato.

b = nil

Nessun deinit viene chiamato. Perché?

Perché la distruzione dell’oggetto può essere invocato solamente quando tutte le variabili che puntano a quell’oggetto vengono distrutte.

a = nil // non ancora distrutto
b = nil // adesso l'oggetto è distrutto

Esistono dei metodi per la gestione di questa singolarità ma ne parlerò in un topic dedicato. Così su due piedi, comunque, dovrebbe cominciare a farti venire qualche sospetto.

Immagina di passare un oggetto X, come riferimento ad un altro oggetto Y, in un punto indefinito dell’applicazione (per esempio da un ViewControllerA ad un ViewControllerB). L’ARC di X verrebbe incrementato. Ora immagina che l’oggetto a cui hai passato X, cioè Y, non venga mai distrutto perché Y ha anche altre variabili che puntano a lui.

Quello che si verrebbe a creare è un insieme di oggetti non più utilizzati (memory leaks) e, che rimarrebbero in memoria in un limbo, in attesa della liberazione che potrà avvenire solo al kill dell’applicazione.

Questi oggetti non distrutti porteranno irrimediabilmente al riempimento della memoria.

N.B. L’aumento di memoria di un’applicazione in maniera continua non è obbligatoriamente frutto di un memory leaks. Esistono degli strumenti appositi per l’analisi di questo problema.

Attenzione alla combinazione fatale tra le struct

Le struct sono indubbiamente più semplici e non causano i problemi di reference counting dato che vengono sempre copiate. Quindi potresti essere indotto a pensare di creare solo strutture.

Facciamo un esempio di un probabile problema.

Abbiamo un Hotel con una Room che ha come proprietà la possibilità di essere occupata o menoSia la Room che l’Hotel sono strutture:

struct Room {
    var isBusy: Bool
}


struct Hotel {
    var name: String
    var room: Room
}

var sheraton = Hotel.init(name: "Sheraton", room: Room.init(isBusy: false))

Ora immagina di passare l’oggetto contenuto nella var sheraton ad un altro e di modificare da questa nuova var la stanza. Un possibile esempio potrebbe essere quello del passaggio di questo oggetto da un ViewController ad un altro.

La modifica del nuovo oggetto non comporterà la modifica del precedente e si creerà una situazione di disomogeneità dei dati prodotti.

var sheraton = Hotel.init(name: "Sheraton", room: Room.init(isBusy: false))


var sheraton2 = sheraton
sheraton.room.isBusy = true


sheraton.room.isBusy // true
sheraton2.room.isBusy // false

La stanza è occupata o no?

Problema facilmente risolvibile con una combinazione tra struct e class:

class Room {
    var isBusy: Bool
    
    init(isBusy: Bool) {
        self.isBusy = isBusy
    }
}


struct Hotel {
    var name: String
    var room: Room
}

var sheraton = Hotel.init(name: "Sheraton", room: Room.init(isBusy: false))


var sheraton2 = sheraton
sheraton.room.isBusy = true


sheraton.room.isBusy // true
sheraton2.room.isBusy // true

In questo modo, l’hotel, che serve solo da contenitore può essere strutturato come una semplice struct. Mentre tutti i suoi dati passibili a modifica e non direttamente inglobati nella struttura, come class.

Considerazioni

Quindi struct o class?

Come si può ben capire, non esiste un regola generale. Tutto dipende dalla tipologia di problema che hai davanti.

Entrambe le soluzioni comunque portano alla risoluzione del problema. Una volta conosciuti i segreti e come gestirli si tratta di una semplice implementazione corretta.

– Peppe, io ho sempre programmato all’oscuro di quest’argomento e non ho mai avuto problemi!
– Hai perfettamente ragione!

Oggi, in un mercato sempre più in competizione e di fretta, si tende a sottovalutare l’aspetto tecnico delle soluzioni adottate. Gli hardware su cui i software girano sono talmente evoluti ed espansi che l’eventuale problema incorre o si presenta solo in situazioni complesse o stravaganti (per esempio in ambiente multithread).

Quindi il programmatore medio tralascia tutti questi particolari e sviluppa come il cuore lo comanda, il più in fretta possibile.

Alla fine, se difficilmente queste grane si presenteranno, tutto si può ricondurre al: quali sono le differenze tra un Programmatore ed un programmatore?

Come ho già spiegato nel tutorial sulla legge di Demetra, spetta a te scegliere in quale categoria collocarti.

Una cosa comunque è certa.

Se vuoi fare il salto di qualità devi cominciare a ragionare sulle scelte che prendi. Giuste o sbagliate che siano, devi provare a dare una motivazione sul perché stai scegliendo una class/struct o stai scrivendo un codice di 1k righe.

Se ti limiterai a scrivere codice, al primo progetto impegnativo o al primo colloquio con un’azienda “seria” finirai per scontrarti con qualcosa più grande di te.

Buona programmazione!

Legge di Demetra applicata al linguaggio Swift | Non parlare con gli sconosciuti!

Ti ricordi cosa dicevano i tuoi nonni quand’eri bambino? “Peppe, non devi assolutamente parlare con gli sconosciuti!”.

Se da ragazzo non capivo quell’affermazione adesso, che ho un po’ di barba, riesco a concepirne il motivo. Il dialogare con qualche sconosciuto m’avrebbe potuto portare a commettere qualche errore.

Può sembrare una banalità, ma tale affermazione è stata ripresa per filo e per segno dalla maggior parte dei linguaggi di programmazione orientati agli oggetti.

La legge di Demetra o Principio della Conoscenza Minima stabilisce che un oggetto dovrebbe conoscere solamente oggetti strettamente correlati e che dovrebbe interagire solo con quelli che conosce direttamente.

Cosa significa?

Ipotizziamo d’avere 3 oggetti: A, B e C.

Secondo la Law of Demeter, un oggetto A può richiedere un servizio dell’oggetto B (cioè può richiamare un suo metodo) ma, l’oggetto A, non può utilizzare B per raggiungere un servizio dell’oggetto C.

Il perché è abbastanza semplice ed è riconducibile a quello che accade quando si gioca al “telefono senza fili“. Il passaggio da A a C, attraverso un oggetto B, implica la piena conoscenza della struttura di C da parte dell’oggetto A.

Spiegato in maniera più semplice, nel caso C dovesse cambiare la sua struttura interna (nuovi metodi e proprietà), sia A che B dovrebbero modificare l’interazione con tale oggetto.

Come si risolve?

Applicando la legge di Demetra, se A volesse accedere ad un servizio di C attraverso B, l’oggetto B dovrà fornire un servizio (cioè un metodo) che richiami il metodo di C. In questo modo A accederà indirettamente a C attraverso la sola conoscenza di B.

class A {
    var b: B
    func doSomethingWithB() {
        b.doSomething()
    }

    func doSomethingWithC() {
        b.doSomethingWithC()
    }
}

class B {
    var c: C
    func doSomething() {}
    
    func doSomethingWithC() {
        c.doSomething()
    }
}

class C {
    func doSomething() {}
}


var a = A()
a.doSomethingWithB() // CORRETTA APPLICAZIONE DELLA LEGGE DI DEMETRA
a.b.doSomething() // VIOLAZIONE DELLA LEGGE

Proviamo a formalizzare un po’ meglio il tutto e vediamo insieme come applicare la legge di Demetra al linguaggio Swift!

Sei pronto?
Allora cominciamo!

Le regole

La legge di Demetra afferma che ogni metodo M, di un oggetto O, possa invocare solo i metodi dei seguenti tipi di oggetti:

  1. I propri (cioè di self)
  2. Dei suoi parametri
  3. Di ogni oggetto che crea
  4. Dei suoi componenti diretti (le sue proprietà)

Viceversa, un oggetto dovrebbe evitare di invocare metodi di un oggetto ritornato da un altro metodo.

Dato che la maggior parte dei linguaggi di programmazione, tra cui anche il linguaggio Swift, utilizzano la dot notation per accedere ai metodi di un oggetto, queste 5 regole, a loro volta, potrebbero essere ricondotte alla semplice affermazione: “usa un solo punto“.

Esempio

Un’applicazione pratica della legge di Demetra potrebbe essere quella dell’impostazione del DataSource e Delegate della UITableView.

Ipotizziamo d’avere una lista di elementi da visualizzare. Generalmente, quello che si fa, è creare la struttura logica che rappresenti quei dati (quindi una classe o struct):

struct Item {
    var property: String
}

class ItemsContainer {
    var array: [Item] = []
}

Nel nostro caso abbiamo due tipi di modelli. Il primo, Item, che ci rappresenta il singolo dato ed il secondo, ItemsContainer, che gestirà i vari Item e le operazioni su di essi.

Una volta fatto questo, il secondo passaggio è quello della definizione del DataSource e Delegate della tabella.

Partiamo da un errore comune, ammetto che per scopi didattici lo faccio anche io, che è quello di definire il ViewController come sottoscrittore dei protocolli UITableViewDataSource e UITableViewDelegate. Questo genera già un primo problema in quanto, per un altro principio che si chiama di Singola Responsabilità (ne parlerò più avanti), appesantisce il ViewController con codice che non dovrebbe gestire per sua natura.

In generale, dovresti creare un oggetto TableViewController che implementi questi protocolli:

class TableViewController: NSObject, UITableViewDataSource {
    var container: ItemsContainer
    
    init(container: ItemsContainer) {
        self.container = container
    }
    
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.container.array.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        
        let dataToDisplay = self.container.array[indexPath.row].property
        cell.textLabel?.text = dataToDisplay
        return cell
    }
}

Nel codice ci sono delle chiarissime violazioni della legge di Demetra.

Le hai notate?

Sia nel metodo numberOfRowsInSection che nel metodo cellForRowAt stai accedendo all’oggetto array che è un estraneo per l’oggetto UITableViewController. Quindi stai dando troppa conoscenza al TableViewController, il quale, come unico vicino ed amico ha l’oggetto container.

Per la legge di Demetra, nel caso volessi accedere ai dati dell’array, cioè dell’oggetto C partendo dall’oggetto A, dovrebbe essere B a fornirti queste informazioni. Ovvero ItemsContainer dovrebbe fornire sia il numero di elementi presenti che l’Item dato un indice:

struct Item {
    var property: String
}

class ItemsContainer {
    var array: [Item] = []
    
    var numberOfItems: Int {
        return array.count
    }
    
    func item(at index: Int) -> Item {
        return self.array[index]
    }
}


class TableViewController: NSObject, UITableViewDataSource {
    var container: ItemsContainer
    
    init(container: ItemsContainer) {
        self.container = container
    }
    
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.container.numberOfItems
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        
        let dataToDisplay = self.container.item(at: indexPath.row)
        cell.textLabel?.text = dataToDisplay.property

        return cell
    }
}

La Law of Demeter indubbiamente permette di separare meglio le logiche di un’applicazione e di avere, come conseguenza, un codice notevolmente più flessibile.

Per esempio, senza la legge e se avessi voluto cambiare il modello dei dati dell’ItemsContainer, da un semplice Array a Dictionary, avrei dovuto modificare anche la classe TableViewController. Con la legge di Demetra, invece, dovrò solamente riadattare la proprietà numberOfItems ed il metodo item(at index).

Perché?

La legge di Demetra ha una storia molto antica. É stata formulata 30 anni fa ed ovviamente, nel corso degli anni, è stata riadattata e riconsiderata secondo le esigenze moderne (le regole sono sempre le stesse ma le applicazioni cambiano).

A sostegno della LoD sono stati compiuti diversi studi. Per esempio, uno studio condotto ad Ottobre del 1996, intitolato “A Validation of Object-Oriented Design Metrics as Quality Indicators“, evinse due caratteristiche:

  1. Con il probabile aumento delle invocazioni di metodi, da parte del metodo chiamato dalla classe di partenza, si riduce la presenza di bug nel codice. Ovvero la suddivisione crea un codice più snello e di facile comprensione.
  2. Dall’altro lato, con l’aumento della presenza di metodi all’interno di una classe, c’è il rischio che aumenti la presenza di bug. Ovvero aumentano le dimensioni delle classi per via dei metodi wrapper e aumenta la complessità di lettura e analisi (vedi le God Class).

Ad ogni modo, oggi la legge di Demetra è una condizione necessaria per poter sviluppare applicazioni complesse. La suddivisione dei compiti è una prerogativa in tutte quelle applicazioni che fanno uso dell’architettura multilayered dove si da per scontato che un livello comunichi solo con i livelli vicini.

Le leggi si rispettano

Ma fai attenzione a non confonderti perché non è un Pattern, ma una legge.

Un pattern è una soluzione standardizzata per la risoluzione di un determinato set di problemi, la regola è qualcosa che lo prescinde e che sta al di sopra.

Ma vuoi dirmi che dovrei utilizzarla soltanto per questo?

Non considerare queste nozioni come cose astratte o accademiche.

Non pensare che il codice che stai scrivendo, perché riguarda una piccola app o un progetto personale, non debba seguire delle regole, dei pattern o delle metodologie di programmazione definite.

É vero che hai il potere di scrivere il codice come vuoi e comunque vederlo funzionare ma, come dice un detto: non perché puoi fare una cosa vuol dire che devi farla.

legge di Demetra

Devi cominciare ad approcciarti ad ogni applicazione o problema come se tu fossi un medico.

Il medico prima di entrare in sala operatoria, prima di togliere un semplice punto o prima di toccarti segue delle procedure standard. Ovvero si lava e disinfetta le mani ecc. Non può sottrarsi a questo compito perché sa che la dimenticanza o il non seguire una pattern o una legge potrebbe significare la morte del paziente o una complicanza in corso d’opera.

Tu devi cominciare a pensare da professionista.

Quindi, scegli, vuoi essere un medico o un semplice macellaio? (inteso come dispregiato, non come categoria)

Alla prossima e buona programmazione!

Considerazioni

Se vuoi approfondire:

  1. Qui trovi lo studio condotto dalla Northeastern University che ha introdotto la Legge di Demetra.
  2. Qui, Yegor Bugayenko, descrive meglio il concetto dell”usa un solo punto” in ottica moderna. Ti consiglio questa lettura solo se già hai avuto modo di sperimentare la LoD altrimenti potresti essere indotto a pensare che quello che ho scritto non serva a niente.
  3. Qui invece trovi il tutorial da cui ho preso l’esempio del codice.

Changelog

  1. 8/04/2017 – Prima versione del tutorial.