GraphQL vs REST in Swift: What an iOS Developer Should Choose in 2025

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction: REST vs GraphQL — an old battle in the new world of Swift



Once upon a time, in the era of Objective-C and selector-based dispatch, the choice between REST and GraphQL was not so acute. REST was king, and JSON was its faithful squire. But times have changed. Swift has become a language with a strong type system, async/await, Codable, and powerful frameworks like Apollo iOS have emerged. Now the question "REST or GraphQL?" sounds different: "What will bring more benefit to my Swift project?"



In this article, we will not retell Wikipedia. We will take a real example of an iOS application for an educational platform (yes, that very PythonLib) and see how each approach behaves in Swift code. You will learn when REST still rules and when GraphQL is a true savior.



Briefly about the main thing: how REST differs from GraphQL



For those just getting into the topic — basic concepts.



  • REST is an architectural style where each resource (user, course, lesson) has its own URL. You get a fixed data structure that the server decided to give you. Often — a whole bunch of extra stuff.
  • GraphQL is a query language. You decide which fields you need. One entry point (/graphql) and one query can replace several REST queries.


On paper, GraphQL looks like a silver bullet. But in Swift projects, there are nuances that we will now examine in practice.



Example 1: Getting a user profile and their courses



REST approach in Swift (URLSession + Codable)



Let's say we have a classic REST API. To show the profile and list of courses, two requests are needed:



// Modelsstruct 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}

// Network layerfunc 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)}

// Usagelet profile = try await fetchUserProfile(userId: 42)let courses = try await fetchCourses(userId: 42)


Everything is simple and predictable. But there is a problem: we get all profile fields, even if we only need the name and email. And also — two network requests. On a slow connection, this is painful.



GraphQL approach in Swift (Apollo iOS)



Now the same thing, but via GraphQL. First, define the query in a .graphql file:



query GetUserWithCourses($userId: ID!) {    user(id: $userId) {        id        name        email        courses {            id            title            progress        }    }}


Apollo generates typed Swift code automatically. In Xcode it looks like this:



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 {

Blogs

Book Recommendations