Compatibile con Xcode 10

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

ENTRA NEL NOSTRO GRUPPO FACEBOOK

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. 

Cos’è il Codable?

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

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:

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.

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).

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:

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:

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:

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.

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:

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:

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:

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:

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:

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.

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.

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:

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:

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:

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:

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:

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:

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:

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:

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):

E la nostra struct sarà qualcosa del genere:

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! 😎

Start typing and press Enter to search

Le novità del linguaggio swift 4.2Come installare e usare Cocoapods