If we ask ChatGPT what GraphQL is, the answer is: ‘GraphQL is an API query language developed by Facebook. It is described as a revolutionary way of managing client-server communication, allowing the client to request only the necessary data and receive structured responses efficiently.
GraphQL has in fact been adopted not only by Facebook but also by giants such as Airbnb, Atlassian, Shopify, Netflix and PayPal. The core principle is that GraphQL calls can offer better performance than REST calls, especially when it is necessary to combine several structured data.
But how is GraphQL developed in the iOS environment? Is it really as efficient as described?
Apollo CLI on iOS
Apollo CLI facilitates the integration of GraphQL in iOS apps, offering tools for query and data management. Strong typing and cache management make Apollo valuable in development.
The Apollo CLI allows automatic generation of classes, mutations and queries directly from the GraphQL schema. It is available via CocoaPods or Swift Package Manager (SPM). However, the official documentation does not mention some problems that may come up during configuration.
Configuration problems
To install the Apollo CLI, we followed the official instructions carefully, testing both via SPM and the plugin offered for Xcode(v15.3), but the Apollo CLI executable was not included in the project. This prevented the automatic generation of GraphQL models.
Instead, when using CocoaPods, the installation was successful. Nevertheless, the technical choice was to use SPM exclusively.
Consulting online forums, the solutions proposed were:
- Downgrade Xcode
- Manually importing the Apollo CLI executable (solution adopted)
JSON configuration
The creation of the JSON configuration file did not cause any problems.
Generation command:
apollo-ios-cli init --schema-namespace <namespace>
--module-type <type> [--target-name <target name>]
Example of generated JSON configuration:
{
"schemaNamespace" : "GraphQLProject",
"input" : {
"operationSearchPaths" : ["**/*.graphql"],
"schemaSearchPaths" : ["**/*.graphqls"]
},
"output" : {
"testMocks" : { "none" : {} },
"schemaTypes" : {
"moduleType": { "swiftPackageManager": {} },
"path": "./GraphQLSchemas"
},
"operations" : { "inSchemaModule" : {} }
}
}
This file allows you to configure the properties that will handle the code generated by the Apollo CLI. The GraphQL schema can be downloaded directly from a remote URL or defined in local files.
Difference between .graphqls and .graphql files
- .graphqls: Contains the definition of the server-side data structure (entities, enums, interfaces, queries, mutations).
- .graphql: Contains the client-defined queries and mutations.
GraphQL allows the client to request only the necessary data, avoiding the retrieval of unnecessary information unlike the REST API.
Output Management
The output configuration allows you to decide how to generate the packet:
- Embedded in Target
- Swift Package Manager (SPM)
- Other
The choice depends on the project architecture. In a whitelabel app with multiple targets, the solution adopted by us was an SPM package that could be imported with Tuist support.
Query management
Every query defined in .graphql must have a correspondent in the schema. In an ongoing project, it is essential to maintain control over the queries requested and the schema. If a field changes name or is removed, Apollo CLI stops pattern generation, reporting the error with the row number.
How to reduce errors? By using Fragments.
Example without Fragments:
query GetBookById($getBookById: ID!) {
getBookById(bookId: $bookId) {
id
title
}
}
query GetBookByCode($code: Int) {
getBookByCode(code: $code) {
id
title
type
code
author
}
}
If the backend changes the code
field to codeId
, we will have to update both queries.
Example withFragments:
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
}
}
If code
changes in codeId
, we will only need to update BookFragment, avoiding ripetead errors.
However, excessive use of fragments may lead to overly generic queries, compromising the efficiency of requests.
Apollo CLI problems with multiple files
If definitions are split into multiple .graphql files, the Apollo CLI may not recognise fragments defined in a separate file, generating errors.
ChatGPT suggests using:
apollo codegen:generate --target=swift
--includes=GraphQL/**/*.graphql
--localSchemaFile=schema.graphql --output=GraphQLAPI.swift
Here is the test to see if this solution really works.
Generated Swift code
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 generates a separate class for each query, even though the objects are the same. To keep the code more modular, the only way is to use fragments.
Cache Management and Errors
Apollo offers advanced cache management, allowing you to define the behaviour for each call. It is useful to note that the cache is active by default, which may prevent requests from being displayed in debug logs if monitoring libraries are used. However, within fetch
or mutation
, you can check whether the data has been retrieved from the cache.
Error management is robust and customisable, making it easy to detect mismatches between schema and query.
Conclusions
GraphQL’s integration with Apollo on iOS offers a powerful tool for client-server communication, combining efficiency and typing. However, in order to achieve clean and scalable code, it is essential to structure queries correctly and make the most of fragments.
Once its operation is understood, Apollo CLI can become a viable alternative to the REST API.
Main Author: Samantha Giro, Technical Mobile Lead