gRPC в Swift: когда это не просто модно, а нужно
Представьте: вы пишете iOS-приложение для управления умным домом. Команды отправляются каждую секунду, задержка в 100 мс — уже катастрофа. Или вы делаете стриминговый сервис, где видео, аудио и телеметрия летят одновременно. REST тут начнет «тормозить» и курить в сторонке. Знакомьтесь — gRPC в Swift.
Но давайте честно: gRPC — не серебряная пуля. Иногда простой JSON на HTTP/1.1 работает лучше. В этой статье мы разберем, когда gRPC в iOS оправдан, когда лучше оставить REST, и как это выглядит в реальном коде на Swift.
Я не буду лить воду. Только практика, примеры и конкретные сценарии. Поехали.
Что такое gRPC (очень кратко, для тех, кто пропустил)
gRPC — это RPC-фреймворк от Google, который использует Protocol Buffers (protobuf) для сериализации данных и HTTP/2 для транспорта. Он поддерживает:
- Унарные вызовы (как обычный REST)
- Серверный стриминг
- Клиентский стриминг
- Бидирекциональный стриминг
На Swift есть официальная библиотека grpc-swift (Swift Package Manager), которая позволяет и клиенту, и серверу работать на Swift. Да, вы можете написать сервер на Swift с gRPC — это не шутка.
Когда gRPC в Swift — ваш лучший друг
1. Высокая нагрузка и низкая задержка
Если ваше приложение отправляет сотни запросов в секунду (например, трейдинг, гейминг, IoT), gRPC выигрывает за счет:
- HTTP/2 — мультиплексирование, сжатие заголовков
- Бинарный протокол — protobuf быстрее парсится, чем JSON
- Keep-alive соединения — не нужно каждый раз открывать TCP-рукопожатие
Пример из жизни: приложение для мониторинга серверов. Каждую секунду приходит 1000 метрик. На REST — 1000 TCP-соединений, на gRPC — одно долгоживущее. Разница в нагрузке на батарею — в разы.
2. Стриминг данных
REST с его «запрос-ответ» не умеет стримить. А gRPC — умеет. Если вам нужно:
- Получать обновления курса валют в реальном времени
- Стримить аудио/видео
- Получать логи с сервера по мере их появления
Тогда gRPC — ваш выбор. Вот как выглядит серверный стриминг на Swift:
// Определение в .proto
service StreamService {
rpc SubscribeToUpdates(Empty) returns (stream Update) {}
}
// Клиент на Swift
let client = StreamServiceAsyncClient(channel: channel)
let updates = client.subscribeToUpdates(Empty())
for try await update in updates {
print("Получено обновление: \(update.message)")
}
Обратите внимание: for try await — это Swift Concurrency. gRPC-Swift отлично с этим работает.
3. Микросервисы на Swift (да, такое бывает)
Вы можете написать весь бэкенд на Swift (Vapor, Hummingbird) и использовать gRPC для внутренней коммуникации между микросервисами. Плюсы:
- Общий код моделей (protobuf генерирует Swift-структуры)
- Строгая типизация
- Встроенная поддержка таймаутов, ретраев, балансировки
// Сервер на Swift
final class GreeterProvider: GreeterAsyncProvider {
func sayHello(request: HelloRequest, context: GRPCAsyncServerCallContext) async throws -> HelloReply {
return HelloReply.with { $0.message = "Привет, \(request.name)!" }
}
}
Этот код — полноценный gRPC-сервер, который запускается на любом Linux/macOS сервере.
4. Когда у вас строгая контрактная система
Protobuf-файлы — это единый источник правды. Вы описали API в .proto, сгенерировали код для Swift, Go, Python — и все говорят на одном языке. Никаких «ой, а тут поле опциональное, а тут нет».
Для больших команд это спасение. Особенно если клиент и сервер пишут разные люди.
Когда gRPC в Swift — лишняя сложность
Давайте без фанатизма. Есть сценарии, где gRPC только мешает:
1. Простое CRUD-приложение
Если вы делаете список покупок или заметки — REST с JSON будет проще. gRPC требует:
- Установки protobuf-компилятора
- Генерации кода
- Поддержки HTTP/2 на сервере
Для маленького проекта это оверхед, который не окупается.
2. Публичное API для браузеров
Браузеры не умеют нативно работать с gRPC (без gRPC-Web, который добавляет прослойку). Если ваш клиент — веб, лучше оставить REST или GraphQL.
3. Команда не знает protobuf
Если ваши разработчики никогда не работали с Protocol Buffers, первое время будет больно. Ошибки компиляции, странные сообщения, проблемы с совместимостью версий. REST с JSON — понятнее.
Как начать использовать gRPC в Swift: пошаговый чеклист
- Добавьте зависимость через SPM:
https://github.com/grpc/grpc-swift.git - Опишите API в
.protoфайле - Сгенерируйте Swift-код с помощью
protoc-gen-grpc-swift - Создайте клиент или сервер на основе сгенерированных классов
- Запустите — и готово
Вот минимальный пример клиента, который отправляет запрос и получает ответ:
import GRPC
import NIOCore
import NIOPosix
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
defer { try! group.syncShutdownGracefully() }
let channel = try GRPCChannelPool.with(
target: .host("localhost", port: 50051),
transportSecurity: .plaintext,
eventLoopGroup: group
)
let client = EchoAsyncClient(channel: channel)
let response = try await client.sayHello(HelloRequest.with { $0.name = "Мир" })
print("Ответ: \(response.message)")
Видите? Ничего сложного. Это работает из коробки.