Введение в GraphQL и Swift
GraphQL — это язык запросов для API, который позволяет клиентам получать именно те данные, которые им нужны, и ничего лишнего. Для разработчиков iOS и macOS интеграция GraphQL открывает новые возможности для создания эффективных и гибких мобильных приложений. В этом туториале мы на практике разберем, как подключить GraphQL API к Swift-проекту, выполнить базовые запросы и мутации, а также обработать возможные ошибки.
В отличие от традиционного REST, GraphQL позволяет запрашивать несколько ресурсов за один запрос, что особенно важно для мобильных устройств с ограниченным трафиком. Мы будем использовать популярную библиотеку Apollo iOS — самое стабильное и документированное решение для работы с GraphQL на Swift.
Подготовка проекта и установка Apollo iOS
Шаг 1: Создание проекта и установка зависимостей
Откройте Xcode и создайте новый проект iOS (или macOS). Для работы с GraphQL мы установим Apollo iOS через Swift Package Manager (SPM).
- В Xcode выберите
File → Add Packages - Вставьте URL репозитория:
https://github.com/apollographql/apollo-ios.git - Выберите последнюю стабильную версию (на момент написания — 1.15+)
- Добавьте пакет в ваш таргет
Шаг 2: Генерация кода по схеме GraphQL
Apollo iOS использует кодогенерацию: вы описываете запросы в .graphql файлах, а утилита создает типизированный Swift-код. Для этого необходимо:
- Создайте папку
GraphQLв проекте - Поместите туда файл
schema.graphqls(схему вашего API) — обычно ее можно получить через introspection - Создайте файл запроса, например
GetUser.graphql:
query GetUser($id: ID!) {
user(id: $id) {
id
name
email
posts {
title
}
}
}
Затем добавьте Build Phase с запуском кодогенератора. Проще всего установить Apollo CLI через Homebrew:
brew install apollo-ios-cli
И выполнить команду в корне проекта:
./apollo-ios-cli generate
После этого в папке Sources/API появятся сгенерированные Swift-файлы с моделями данных.
Выполнение базовых GraphQL-запросов
Настройка клиента Apollo
Создайте экземпляр клиента Apollo, указав URL вашего GraphQL-эндпоинта. Обычно это делается в AppDelegate или в отдельном сервисном классе:
import Apollo
class NetworkService {
static let shared = NetworkService()
private(set) lazy var apollo: ApolloClient = {
let url = URL(string: "https://your-api.com/graphql")!
return ApolloClient(url: url)
}()
}
Запрос данных (Query)
Теперь выполним запрос на получение пользователя. Сгенерированный код создаст структуру GetUserQuery:
import Foundation
class UserViewModel: ObservableObject {
@Published var user: User?
@Published var errorMessage: String?
func fetchUser(id: String) {
NetworkService.shared.apollo.fetch(query: GetUserQuery(id: id)) { [weak self] result in
DispatchQueue.main.async {
switch result {
case .success(let graphQLResult):
if let userData = graphQLResult.data?.user {
self?.user = User(
id: userData.id,
name: userData.name,
email: userData.email
)
}
case .failure(let error):
self?.errorMessage = "Ошибка: \(error.localizedDescription)"
}
}
}
}
}
Обратите внимание: мы используем @Published для реактивного обновления UI в SwiftUI. Для UIKit можно использовать делегаты или Combine.
Мутации и обработка ошибок
Выполнение мутации (Mutation)
Мутации в GraphQL используются для изменения данных (создание, обновление, удаление). Создадим файл CreatePost.graphql:
mutation CreatePost($title: String!, $content: String!) {
createPost(input: { title: $title, content: $content }) {
id
title
createdAt
}
}
После генерации кода, вызов мутации выглядит так:
func createPost(title: String, content: String) {
NetworkService.shared.apollo.perform(mutation: CreatePostMutation(title: title, content: content)) { result in
switch result {
case .success(let graphQLResult):
if let post = graphQLResult.data?.createPost {
print("Пост создан: \(post.title)")
}
if let errors = graphQLResult.errors {
for error in errors {
print("GraphQL ошибка: \(error.message)")
}
}
case .failure(let error):
print("Сетевая ошибка: \(error)")
}
}
}
Обработка ошибок: сетевые и GraphQL-ошибки
Важно различать два типа ошибок:
- Сетевые ошибки — проблемы с соединением, таймауты (обрабатываются в
.failure) - GraphQL-ошибки — ошибки бизнес-логики сервера (содержатся в
graphQLResult.errors)
Пример универсального обработчика:
func handleResult<T>(_ result: Result<GraphQLResult<T>, Error>, completion: (T?, String?) -> Void) {
switch result {
case .success(let graphQLResult):
if let data = graphQLResult.data {
completion(data, nil)
} else if let error = graphQLResult.errors?.first {
completion(nil, error.message)
}
case .failure(let error):
completion(nil, "Сетевая ошибка: \(error.localizedDescription)")
}
}
Оптимизация для реальных проектов
Кэширование и нормализация данных
Apollo iOS поддерживает кэширование по умолчанию. Вы можете настроить политику кэширования:
let store = ApolloStore()
let client = ApolloClient(url: url, store: store)
// Использовать кэш, затем запрос к серверу
let cachePolicy = CachePolicy.returnCacheDataAndFetch
apollo.fetch(query: query, cachePolicy: cachePolicy) { result in
// ...
}
Подписки (Subscriptions) через WebSocket
Для real-time обновлений используйте WebSocket-транспорт. Установите дополнительный