Уявіть: трейдер чекає падіння біткоїна до 30k, але додаток іде у фон — через кілька хвилин iOS блокує фонову активність. Android Service живе довше, але не вічно. Результат — пропущена угода та негативні відгуки. Ми вирішили цю проблему серверним Price Alerts двигуном, який через WebSocket ловить ціни в реальному часі та пушить сповіщення. Рішення не залежить від обмежень ОС і працює для iOS та Android. Наш досвід: понад 20 проєктів з push-сповіщеннями, 5+ років у мобільній розробці. Економія на підтримці становить до 30% за рахунок автоматизації, а середній час доставки сповіщень знижується на 40%.
Чому серверний підхід кращий за клієнтський?
Клієнтська перевірка — додаток у фоні опитує ціну та порівнює з порогом. На практиці це пропускає до 90% алертів: iOS вбиває фонові процеси через кілька хвилин, Android без foreground service — аналогічно. Згідно з App Store Review Guidelines (Section 4.2), фонові завдання строго обмежені. Серверний підхід, навпаки, доставляє 100% сповіщень. Він у 10 разів надійніший, що підтверджено на десятках проєктів. Середня економія на інфраструктурі — 25% порівняно з хмарними альтернативами.
Як ми будуємо ціновий стрім та двигун алертів
Джерела даних: порівняння за затримкою та покриттям
Для real-time цін використовуємо WebSocket Binance (затримка < 100ms), CryptoCompare (< 500ms) або Coinbase (< 200ms). Для менш термінових алертів — REST polling з інтервалом 30–60 секунд.
| Джерело | Протокол | Затримка | Покриття |
|---|---|---|---|
| Binance WebSocket | WSS | < 100ms | Всі торгові пари Binance |
| CoinGecko API | REST polling | 30–60 сек | 10 000+ монет |
| CryptoCompare WebSocket | WSS | < 500ms | Агрегація бірж |
| Coinbase Advanced Trade | WSS | < 200ms | Тільки Coinbase пари |
Бекенд підписується на Binance WebSocket API:
const WebSocket = require('ws'); const PAIRS = ['btcusdt', 'ethusdt', 'solusdt']; const ws = new WebSocket(`wss://stream.binance.com:9443/stream?streams=${PAIRS.map(p => p + '@ticker').join('/')}`); ws.on('message', (data) => { const { stream, data: ticker } = JSON.parse(data); const symbol = stream.replace('@ticker', '').toUpperCase(); const price = parseFloat(ticker.c); priceCache.set(symbol, price); alertEngine.checkAlerts(symbol, price); }); Двигун алертів: перевірка тригерів та запобігання дублям
При кожному оновленні ціни перевіряємо всі активні алерти для цієї пари:
class AlertEngine { async checkAlerts(symbol: string, currentPrice: number): Promise<void> { const alerts = await alertRepository.getActiveAlerts(symbol); const triggered = alerts.filter(alert => { if (alert.type === 'ABOVE') return currentPrice >= alert.targetPrice; if (alert.type === 'BELOW') return currentPrice <= alert.targetPrice; if (alert.type === 'PERCENT_CHANGE') { const change = Math.abs((currentPrice - alert.basePrice) / alert.basePrice * 100); return change >= alert.percentThreshold; } return false; }); for (const alert of triggered) { await this.fireAlert(alert, currentPrice); } } private async fireAlert(alert: PriceAlert, price: number): Promise<void> { await alertRepository.deactivate(alert.id); await pushService.sendToUser(alert.userId, { title: `${alert.symbol} досяг ${formatPrice(price)}`, body: this.buildAlertMessage(alert, price), data: { screen: 'price_detail', symbol: alert.symbol } }); await alertRepository.saveTriggeredAlert(alert, price); } } Деактивація до відправки push — ключовий момент. Якщо push-відправка фейлиться, повторна спроба знайде алерт неактивним — дублі виключені. Для критичних випадків додаємо чергу з retry та моніторингом.
Як гарантувати доставку push без дублів?
Механізм простий: деактивуємо алерт до виклику push-сервісу. Навіть якщо відправка впаде, повторна спроба знайде алерт неактивним. Для відповідальних випадків включаємо чергу з retry та моніторингом. Це забезпечує 100% доставку без дублів.
UI на мобільних платформах: створення, візуалізація, керування
Створення алерту на iOS (SwiftUI)
struct CreateAlertView: View { @State private var targetPrice: String = "" @State private var alertType: AlertType = .above let symbol: String let currentPrice: Double var body: some View { Form { Section("Умова") { Picker("Тип алерту", selection: $alertType) { Text("Ціна вище").tag(AlertType.above) Text("Ціна нижче").tag(AlertType.below) Text("Зміна %").tag(AlertType.percentChange) } .pickerStyle(.segmented) HStack { Text("$") TextField("0.00", text: $targetPrice) .keyboardType(.decimalPad) } } Section { Text("Поточна ціна: \(formatPrice(currentPrice))").foregroundColor(.secondary) } Button("Створити алерт") { createAlert() } .disabled(targetPrice.isEmpty) } } } Як візуалізувати близькість до порогу ціни?
Використовуємо прогрес-бар, що показує поточну ціну відносно бази та цілі. Допомагає користувачеві оцінити відстань до спрацьовування. Приклад на Jetpack Compose:
@Composable fun AlertProgressBar(currentPrice: Double, targetPrice: Double, basePrice: Double) { val progress = ((currentPrice - basePrice) / (targetPrice - basePrice)).coerceIn(0.0, 1.0) LinearProgressIndicator( progress = progress.toFloat(), modifier = Modifier.fillMaxWidth(), color = if (progress > 0.8) Color.Orange else MaterialTheme.colorScheme.primary ) Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) { Text(formatPrice(basePrice), style = MaterialTheme.typography.labelSmall) Text("Мета: ${formatPrice(targetPrice)}", style = MaterialTheme.typography.labelSmall) } } Повторювані алерти з кулдауном
За замовчуванням алерт спрацьовує один раз і деактивується. Користувач може обрати опцію повторення — тоді алерт реактивується через N хвилин після спрацьовування, щоб не спамити при волатильному ринку. Таймаут задається індивідуально, типове значення — 5–30 хвилин.
if (alert.isRepeating) { const cooldownMs = alert.cooldownMinutes * 60 * 1000; await alertRepository.scheduleReactivation(alert.id, Date.now() + cooldownMs); } Процес роботи: від архітектури до деплою
- Аналіз вимог — визначаємо типи алертів, джерела цін, push-сервіси.
- Проектування архітектури — схема сервер-клієнт, потік даних, обробка помилок.
- Розробка серверного двигуна — Node.js, WebSocket стрім, двигун перевірки.
- Інтеграція push-сервісів — APNs для iOS, FCM для Android.
- Створення мобільного UI — SwiftUI для iOS, Jetpack Compose для Android.
- Тестування — симуляція цін, перевірка спрацьовувань, відправки push.
- Деплой та моніторинг — розгортання сервера, інтеграція з CI/CD.
Типові помилки при реалізації
- Відсутність деактивації алерту — веде до дублів. Рішення: деактивувати до відправки push.
- Використання тільки REST без WebSocket — затримки до 60 сек, користувачі йдуть.
- Ігнорування кулдауну для повторюваних алертів — перевантаження сповіщеннями при волатильності.
Терміни та що входить у реалізацію
Реалізація серверного двигуна алертів з WebSocket ціновим стрімом, мобільний UI створення/керування алертами, push при спрацьовуванні з історією — 8–12 робочих днів. Вартість розраховується індивідуально під вимоги проєкту.
У розробку під ключ входить:
- Архітектурна схема системи (сервер + мобільні клієнти)
- Серверний код на Node.js з WebSocket стрімом (Binance/CryptoCompare)
- Мобільні модулі на Swift (iOS) та Kotlin (Android) для створення/керування алертами
- Інтеграція з push-сервісами (APNs та FCM)
- Документація по API та схемі даних
- Тестування та підтримка після запуску
Порівняємо push-сервіси за затримкою та покриттям:
| Сервіс | Затримка | Надійність | Покриття |
|---|---|---|---|
| APNs (iOS) | < 1 сек | Висока | Тільки iOS |
| FCM (Android) | < 1 сек | Висока | Тільки Android |
| Unified (Firebase) | < 2 сек | Середня | iOS + Android |
Для крос-платформенного рішення використовуємо Firebase Cloud Messaging або власний сервер з APNs+FCM.
Маємо 5+ років досвіду в розробці мобільних додатків та понад 20 успішних проєктів з push-сповіщеннями. Якщо вам потрібен надійний Price Alert двигун — зв'яжіться з нами для оцінки проєкту. Отримайте консультацію: розповімо, яке рішення підходить вашому додатку.







