GraphQL con Apollo CLI su iOS: Quello che ChatGPT e la documentazione non dicono

GraphQL with Apollo CLI

Se chiediamo a ChatGPT cos’è GraphQL, la risposta è la seguente: “GraphQL è un linguaggio di query per API sviluppato da Facebook”. Viene descritto come un modo rivoluzionario di gestire la comunicazione client-server, consentendo al client di richiedere solo i dati necessari e ricevere risposte strutturate in modo efficiente.

Non a caso, GraphQL è stato adottato non solo da Facebook ma anche da colossi come Airbnb, Atlassian, Shopify, Netflix e PayPal. L’idea di base è che le chiamate GraphQL possano offrire migliori performance rispetto alle chiamate REST, soprattutto quando occorre combinare più dati strutturati.

Ma come si sviluppa GraphQL in ambito iOS? È davvero così efficiente come descritto?

Apollo CLI su iOS

Apollo CLI facilita l’integrazione di GraphQL nelle app iOS, offrendo strumenti per la gestione delle query e dei dati. La forte tipizzazione e la gestione della cache rendono Apollo un alleato prezioso nello sviluppo.

Apollo CLI permette di generare automaticamente classi, mutazioni e query direttamente dallo schema GraphQL. È disponibile tramite CocoaPods o Swift Package Manager (SPM). Tuttavia, la documentazione ufficiale non menziona alcuni problemi che possono sorgere in fase di configurazione.

Problemi di configurazione

Abbiamo seguito attentamente la documentazione di Apollo CLI per installarlo sia tramite SPM che tramite il plugin Xcode (v15.3), ma l’eseguibile di Apollo CLI non veniva incluso nel progetto. Questo ha impedito la generazione automatica dei modelli GraphQL.

Utilizzando CocoaPods, l’installazione andava invece a buon fine. Ciononostante, la scelta tecnica era di utilizzare esclusivamente SPM.

Dai forum, le soluzioni proposte erano:

  • Effettuare il downgrade di Xcode
  • Importare manualmente l’eseguibile di Apollo CLI (soluzione adottata)

Configurazione del JSON

La creazione del file di configurazione JSON non ha invece generato problemi.

Comando di generazione:

apollo-ios-cli init --schema-namespace <namespace>

--module-type <type> [--target-name <target name>]

Esempio di configurazione JSON generato:

{

  "schemaNamespace" : "GraphQLProject",

  "input" : {

    "operationSearchPaths" : ["**/*.graphql"],

    "schemaSearchPaths" : ["**/*.graphqls"]

  },

  "output" : {

    "testMocks" : { "none" : {} },

    "schemaTypes" : {

      "moduleType": { "swiftPackageManager": {} },

      "path": "./GraphQLSchemas"

    },

    "operations" : { "inSchemaModule" : {} }

  }

}

Questo file permette di configurare le proprietà che gestiranno il codice generato da Apollo CLI. Lo schema GraphQL può essere scaricato direttamente da un URL remoto o definito in file locali.

Differenza tra file .graphqls e .graphql

  • .graphqls: Contiene la definizione della struttura dati lato server (entità, enum, interfacce, query, mutation)
  • .graphql: Contiene le query e mutation definite dal client

GraphQL consente al client di richiedere solo i dati necessari, evitando il recupero di informazioni inutili come accade spesso con API REST. 

Gestione dell’output

La configurazione dell’output permette di decidere come generare il pacchetto:

  • Embedded in Target
  • Swift Package Manager (SPM)
  • Other

La scelta dipende dall’architettura del progetto. In un’app whitelabel con più target, la soluzione adottata è stata un pacchetto SPM importabile con il supporto di Tuist.ple targets, the solution adopted by us was an SPM package that could be imported with Tuist support.

Query management

Gestione delle query

Ogni query definita nel .graphql deve avere un corrispettivo nello schema. In un progetto in evoluzione, è essenziale mantenere il controllo sulle query richieste e sullo schema. Se un campo cambia nome o viene rimosso, Apollo CLI blocca la generazione dei modelli, segnalando l’errore con il numero di riga.

Come ridurre gli errori? Utilizzando Fragments.

Esempio senza fragment:

query GetBookById($getBookById: ID!) {

  getBookById(bookId: $bookId) {

    id

    title

  }

}

query GetBookByCode($code: Int) {

  getBookByCode(code: $code) {

    id

    title

    type

    code

    author

  }

}

Se il backend cambia il campo code in codeId, dovremo aggiornare entrambe le query.

Soluzione con fragment:

fragment BookFragment on Book {

  id

  title

  type

  code

  author

}

query GetBookById($getBookById: ID!) {

  getBookById(bookId: $bookId) {

    ...BookFragment

  }

}

query GetBookByCode($code: Int) {

  getBookByCode(code: $code) {

    …BookFragment

  }

}

Ora, se code cambia in codeId, basta aggiornare solo BookFragment, evitando errori ripetuti.

Tuttavia, un uso eccessivo dei fragment può portare a query troppo generiche, compromettendo l’efficienza delle richieste.

Problemi di Apollo CLI con file multipli

Se le definizioni sono divise in più file .graphql, Apollo CLI potrebbe non riconoscere i fragment definiti in un file separato, generando errori.

ChatGPT suggerisce di usare:

apollo codegen:generate --target=swift

--includes=GraphQL/**/*.graphql

--localSchemaFile=schema.graphql --output=GraphQLAPI.swift

Di seguito il test per verificare se questa soluzione funzioni davvero.

Il codice Swift generato

import Apollo

class BookService {

    private let client = ApolloClient(url: URL(string: "[BASEURL]")!)

    func fetchBooks(completion: @escaping (Result<[Book], Error>) -> Void) {

        client.fetch(query: GetBooksQuery()) { result in

            switch result {

            case .success(let graphQLResult):

                if let books = graphQLResult.data?.books {

                    completion(.success(books.map { $0.fragments.bookFragment }))

                } else if let errors = graphQLResult.errors {

                    completion(.failure(errors.first!))

                }

            case .failure(let error):

                completion(.failure(error))

            }

        }

    }

}

Apollo genera una classe separata per ogni query, anche se gli oggetti sono gli stessi. Per mantenere il codice più modulare, l’unico modo è utilizzare i fragment.

Gestione della cache ed errori

Apollo offre una gestione avanzata della cache, permettendo di definire il comportamento per ogni chiamata. È utile tenere presente che la cache è attiva di default, il che potrebbe impedire di visualizzare le richieste nei log di debug se si usano librerie di monitoraggio. Tuttavia, all’interno della fetch o mutation, si può verificare se il dato è stato recuperato dalla cache.

La gestione degli errori è robusta e personalizzabile, consentendo di individuare facilmente discrepanze tra schema e query.

Conclusioni

L’integrazione di GraphQL con Apollo su iOS offre uno strumento potente per la comunicazione client-server, combinando efficienza e tipizzazione. Tuttavia, per ottenere un codice pulito e scalabile, è essenziale strutturare correttamente le query e sfruttare al meglio i fragment

Una volta compreso il suo funzionamento, Apollo CLI può diventare una valida alternativa alle REST API.


Autore: Samantha Giro, Technical Mobile Lead

Vuoi saperne di più sui nostri servizi? Compila il modulo e fissa un incontro con il nostro team!