Уявіть: трейдер чекає падіння біткоїна до 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 двигун — зв'яжіться з нами для оцінки проєкту. Отримайте консультацію: розповімо, яке рішення підходить вашому додатку.







