Introducción: REST vs GraphQL — la vieja batalla en el nuevo mundo de Swift
Hace tiempo, en la era de Objective-C y la despacho por selectores, la elección entre REST y GraphQL no era tan crucial. REST era el rey, y JSON su fiel escudero. Pero los tiempos han cambiado. Swift se convirtió en un lenguaje con un sistema de tipos fuerte, aparecieron async/await, Codable y potentes frameworks como Apollo iOS. Ahora la pregunta «¿REST o GraphQL?» suena diferente: «¿Qué le dará más beneficio a mi proyecto Swift?».
En este artículo no vamos a repetir la wikipedia. Tomaremos un ejemplo real de una aplicación iOS para una plataforma educativa (sí, esa misma PythonLib) y veremos cómo se comporta cada enfoque en código Swift. Descubrirás cuándo REST sigue siendo el rey y cuándo GraphQL es el verdadero salvador.
Lo esencial: en qué se diferencia REST de GraphQL
Para quienes recién se adentran en el tema — conceptos básicos.
- REST — es un estilo arquitectónico donde cada recurso (usuario, curso, lección) tiene su propia URL. Obtienes una estructura de datos fija que el servidor decide enviarte. A menudo, un montón de datos innecesarios.
- GraphQL — es un lenguaje de consultas. Tú decides qué campos necesitas. Un único punto de entrada (
/graphql) y una sola consulta puede reemplazar varias solicitudes REST.
En teoría, GraphQL parece una bala de plata. Pero en proyectos Swift hay matices que analizaremos en la práctica.
Ejemplo 1: Obtener el perfil del usuario y sus cursos
Enfoque REST en Swift (URLSession + Codable)
Supongamos que tenemos una API REST clásica. Para mostrar el perfil y la lista de cursos, necesitamos dos solicitudes:
// Modelosstruct UserProfile: Codable { let id: Int let name: String let email: String let avatarURL: String}
struct Course: Codable { let id: Int let title: String let progress: Double}
// Capa de redfunc fetchUserProfile(userId: Int) async throws -> UserProfile { let url = URL(string: "https://api.pythonlib.ru/users/\\(userId)")! let (data, _) = try await URLSession.shared.data(from: url) return try JSONDecoder().decode(UserProfile.self, from: data)}
func fetchCourses(userId: Int) async throws -> [Course] { let url = URL(string: "https://api.pythonlib.ru/users/\\(userId)/courses")! let (data, _) = try await URLSession.shared.data(from: url) return try JSONDecoder().decode([Course].self, from: data)}
// Usolet profile = try await fetchUserProfile(userId: 42)let courses = try await fetchCourses(userId: 42)Todo es simple y predecible. Pero hay un problema: obtenemos todos los campos del perfil, incluso si solo necesitamos el nombre y el email. Además, dos solicitudes de red. En una conexión lenta, esto duele.
Enfoque GraphQL en Swift (Apollo iOS)
Ahora lo mismo, pero con GraphQL. Primero definimos la consulta en un archivo .graphql:
query GetUserWithCourses($userId: ID!) { user(id: $userId) { id name email courses { id title progress } }}Apollo genera código Swift tipado automáticamente. En Xcode se ve así:
import Apollo
let apolloClient = ApolloClient(url: URL(string: "https://api.pythonlib.ru/graphql")!)
func fetchUserWithCourses(userId: Int) async throws -> GetUserWithCoursesQuery.Data { return try await withCheckedThrowingContinuation { continuation in apolloClient.fetch(query: GetUserWithCoursesQuery(userId: userId)) { result in switch result { case .success(let graphQLResult): if let data = graphQLResult.data { continuation.resume(returning: data) } else if let errors = graphQLResult.errors {