Почему WebSockets — это суперсила для Swift-разработчика?
Представьте: вы открываете мессенджер, и сообщения приходят мгновенно. Никаких бесконечных обновлений страницы, никаких «потяни, чтобы обновить». Это магия real-time, и в Swift она реализуется с помощью WebSockets. Если вы до сих пор использовали только HTTP-запросы, вы упускаете целый мир возможностей: онлайн-чаты, биржевые котировки, живые уведомления, многопользовательские игры.
В этой статье мы разберём, как добавить WebSockets в ваше Swift-приложение. Никакой воды — только код, который можно скопировать и запустить. К концу вы сможете написать свой первый real-time чат меньше чем за час. Поехали!
Что такое WebSockets и чем они лучше REST API?
WebSockets — это протокол двусторонней связи между клиентом и сервером. В отличие от REST, где клиент постоянно «дёргает» сервер вопросами, WebSocket устанавливает постоянное соединение. Сервер сам может отправлять данные, когда они появляются. Это как разница между тем, чтобы постоянно звонить другу «Ты уже пришёл?» и тем, чтобы просто открыть дверь и впустить его.
Для Swift-разработчика главное преимущество — экономия трафика и ресурсов. Вместо десятков HTTP-запросов в минуту — одно соединение. Идеально для трейдинговых приложений, онлайн-игр, мониторинга систем.
Когда стоит использовать WebSockets в iOS?
- Онлайн-чаты — сообщения должны доставляться мгновенно.
- Биржевые котировки — цена меняется каждую секунду.
- Многопользовательские игры — синхронизация действий игроков.
- Уведомления в реальном времени — новый лайк, заказ, сообщение.
- IoT-приложения — данные с датчиков поступают непрерывно.
Но не стоит применять WebSockets везде. Если вам нужно просто загрузить список товаров — обычный HTTP GET справится лучше. WebSocket оправдан, когда данные обновляются чаще, чем раз в 10 секунд.
Настройка WebSocket в Swift: с нуля до первого соединения
В Swift есть встроенная поддержка WebSockets — класс URLSessionWebSocketTask. Никаких сторонних библиотек не нужно. Давайте напишем простой клиент.
Шаг 1. Создаём WebSocket-соединение
import Foundation
class WebSocketManager: NSObject {
private var webSocketTask: URLSessionWebSocketTask?
private let session: URLSession
override init() {
self.session = URLSession(configuration: .default, delegate: nil, delegateQueue: OperationQueue())
super.init()
}
func connect(to url: URL) {
webSocketTask = session.webSocketTask(with: url)
webSocketTask?.resume()
print("WebSocket соединение установлено")
receiveMessage()
}
func disconnect() {
webSocketTask?.cancel(with: .normalClosure, reason: nil)
print("Соединение закрыто")
}
}
Это базовая обёртка. Метод resume() запускает соединение — без него ничего не произойдёт. Запомните это как мантру: всегда вызывайте resume().
Шаг 2. Отправляем и получаем сообщения
extension WebSocketManager {
func sendMessage(_ text: String) {
let message = URLSessionWebSocketTask.Message.string(text)
webSocketTask?.send(message) { error in
if let error = error {
print("Ошибка отправки: \(error.localizedDescription)")
}
}
}
private func receiveMessage() {
webSocketTask?.receive { [weak self] result in
switch result {
case .success(let message):
switch message {
case .string(let text):
print("Получено сообщение: \(text)")
case .data(let data):
print("Получены данные: \(data)")
@unknown default:
print("Неизвестный тип сообщения")
}
// Рекурсивно слушаем следующее сообщение
self?.receiveMessage()
case .failure(let error):
print("Ошибка получения: \(error.localizedDescription)")
}
}
}
}
Обратите внимание: receive() вызывается один раз. Чтобы слушать сообщения постоянно, мы вызываем его рекурсивно после каждого получения. Классический паттерн для WebSockets.
Шаг 3. Используем на практике
let manager = WebSocketManager()
let wsURL = URL(string: "wss://echo.websocket.org")! // Тестовый сервер
manager.connect(to: wsURL)
manager.sendMessage("Привет, WebSocket!")
Запустите этот код в playground или в реальном приложении — и вы увидите, как сообщение возвращается обратно. Это ваш первый real-time обмен данными на Swift!
Продвинутые техники: пинг-понг и переподключение
В реальном мире соединения рвутся. Сервер может упасть, интернет — пропасть. Чтобы приложение было надёжным, нужно добавить:
Автоматический пинг (heartbeat)
func startPing() {
Timer.scheduledTimer(withTimeInterval: 30, repeats: true) { [weak self] _ in
self?.webSocketTask?.sendPing { error in
if let error = error {
print("Ping failed: \(error.localizedDescription)")
// Здесь можно инициировать переподключение
} else {
print("Ping успешен")
}
}
}
}
Пинг-понг сообщает серверу, что соединение живо, и проверяет, не оборвалась ли связь. Рекомендуемый интервал — 20-30 секунд.
Автоматическое переподключение
func connectWithRetry(to url: URL, maxRetries: Int = 5) {
var retries = 0
func attempt() {
guard retries < maxRetries else {
print("Не удалось подключиться после \(maxRetries) попыток")
return
}
connect(to: url)
retries += 1
// Если соединение упало, через 2 секунды пробуем снова
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
if self.webSocketTask?.state == .completed {
attempt()
}
}
}
attempt()
}
Это простой, но эффективный механизм. В production стоит добавить exponential backoff — увеличивать задержку между попытками.
Создаём real-time чат на Swift: полный пример
Давайте соберём всё вместе и напишем мини-чат с использованием WebSockets. Для примера возьмём публичный сервер wss://echo.websocket.org, который возвращает то, что вы отправили.
Класс ChatManager
import Foundation
protocol ChatManagerDelegate: AnyObject {
func didReceiveMessage(_ text: String)
func didConnect()
func didDisconnect()
}
class ChatManager: NSObject {
private var webSocketTask: URLSessionWebSocketTask?
private let session: URLSession
weak var delegate: ChatManagerDelegate?
override init() {
self.session = URLSession(configuration: .default)
super.init()
}
func connect(to url: URL) {
webSocketTask = session.webSocketTask(with: url)
webSocketTask?.