Live Activity для отслеживания цены криптовалюты на iOS
Представьте: трейдер видит, что биткоин резко пошёл вниз, но не может мгновенно открыть приложение — экран заблокирован. К тому времени, как он разблокирует iPhone, цена может уйти ещё на несколько процентов. Live Activity решает эту проблему: актуальная цена и изменение за 24 часа отображаются прямо на Lock Screen и в Dynamic Island (iPhone 14 Pro и новее). Пользователь видит движение, не совершая ни одного тапа. Мы внедрили это в 15+ крипто-трекинговых проектах — делимся архитектурой.
Почему Live Activity, а не обычный виджет?
Виджеты на Home Screen обновляются раз в 15–60 минут, что неприемлемо для криптовалют. Push-уведомления требуют внимания и могут раздражать. Live Activity — золотая середина: данные обновляются каждые несколько секунд (через ActivityKit Push), занимают минимум места и не требуют от пользователя действия. Это позволяет трейдеру реагировать на изменения быстрее — время реакции сокращается на 40% по нашим данным.
ActivityKit: ключевые ограничения
Live Activity создаётся через ActivityKit. Важно понять принципиальное ограничение до старта разработки: Activity можно запустить только из самого приложения, когда оно на переднем плане. Нельзя стартовать Activity из фонового процесса или push-уведомления. Обновлять — можно через ActivityKit API или через push (ActivityKit Push Update).
Максимальное время жизни одной Activity — 12 часов (система может завершить раньше). Данные ActivityAttributes — статические на всё время жизни. Данные ContentState — динамические, именно они обновляются.
Для криптовиджета архитектура выглядит так:
struct CryptoActivityAttributes: ActivityAttributes { public struct ContentState: Codable, Hashable { var price: Double var change24h: Double var lastUpdated: Date } var symbol: String // статично: BTC, ETH и т.д. var baseCurrency: String // статично: USD } Запуск и обновление
let attributes = CryptoActivityAttributes(symbol: "BTC", baseCurrency: "USD") let initialState = CryptoActivityAttributes.ContentState( price: 67_430.0, change24h: 2.3, lastUpdated: .now ) let activity = try Activity<CryptoActivityAttributes>.request( attributes: attributes, content: .init(state: initialState, staleDate: Date().addingTimeInterval(60)), pushType: .token // если планируем обновлять через push ) staleDate — момент, когда система считает данные устаревшими и может показать специальный UI. Для цены крипты ставим 60–120 секунд.
Обновление через локальный код:
let updatedState = CryptoActivityAttributes.ContentState( price: newPrice, change24h: newChange, lastUpdated: .now ) await activity.update(.init(state: updatedState, staleDate: Date().addingTimeInterval(60))) Как работают обновления через ActivityKit Push?
Для real-time обновлений цены нужен backend, который отправляет ActivityKit Push Notification — это отдельный тип push, не APNs-уведомление. Payload выглядит так:
{ "aps": { "timestamp": 1699000000, "event": "update", "content-state": { "price": 68100.0, "change24h": 2.8, "lastUpdated": 1699000000 }, "alert": { "title": "BTC", "body": "$68,100" } } } Токен для ActivityKit Push — отдельный от обычного APNs-токена. Приложение получает его через activity.pushTokenUpdates и должно отправить на сервер. Если токен не обновляется на сервере после рестарта Activity — обновления перестают приходить.
Dynamic Island: компактный и развёрнутый вид
SwiftUI-вёрстка для Dynamic Island делится на несколько представлений: compactLeading, compactTrailing, minimal, expanded. Каждое — отдельная SwiftUI View. Ограничение по размеру для compact-видов жёсткое — буквально несколько пикселей, никаких списков.
.dynamicIsland { context in DynamicIsland { DynamicIslandExpandedRegion(.leading) { Text(context.attributes.symbol).font(.headline) } DynamicIslandExpandedRegion(.trailing) { Text(context.state.change24h >= 0 ? "↑" : "↓") .foregroundColor(context.state.change24h >= 0 ? .green : .red) } DynamicIslandExpandedRegion(.center) { Text("$\(context.state.price, format: .number.precision(.fractionLength(2)))") .font(.title2) } } compactLeading: { Text(context.attributes.symbol).font(.caption2) } compactTrailing: { Text("$\(Int(context.state.price))").font(.caption2) } minimal: { Text(context.attributes.symbol.prefix(1)) } } | Метод обновления | Задержка | Требует сервер | Офлайн-режим |
|---|---|---|---|
| Локальное (update) | Мгновенно | Нет | Да |
| Push-обновление | 1–5 секунд | Да | Нет |
Как обновлять данные без сервера?
Если приложение уже использует WebSocket или другой real-time канал, можно обновлять Activity напрямую: получили новую цену — вызвали activity.update(_:). Это проще и дешевле, чем разворачивать инфраструктуру для ActivityKit Push. Минус — приложение должно быть активно хотя бы фоново (Background fetch или WebSocket с keep-alive). Для крипто-трекинга мы обычно комбинируем: локальное обновление для сиюминутных данных и push как запасной канал.
Типичные ошибки и как их избежать
Частые проблемы при внедрении Live Activity
- Забыли обновить push-токен — если Activity перезапускается, токен меняется. Сервер должен обрабатывать обновления, иначе push-уведомления перестают доставляться.
- Слишком большой staleDate — при цене крипты более 2 минут устаревания пользователь видит некорректные данные. Ставьте 60 секунд.
- Не обработали завершение Activity системой — если система завершает Activity раньше 12 часов (например, при нехватке памяти), приложение должно корректно реагировать и перезапускать её при следующем foreground.
Что нужно учитывать при запуске Activity?
- Activity можно запустить только на переднем плане — это ключевое ограничение. Планируйте логику инициализации при входе пользователя в приложение.
- Используйте
staleDateразумно: для криптовалют 60–90 секунд оптимально. - Убедитесь, что сервер может обрабатывать смену push-токенов.
Что входит в работу
- Создание ActivityKit extension с
ActivityAttributesиContentState - SwiftUI-вёрстка для Lock Screen, Dynamic Island (compact, minimal, expanded)
- Запуск и завершение Activity из основного приложения
- Настройка ActivityKit Push обновлений (требует серверной части)
- Обработка устаревания данных (
staleDate) - Тестирование на iPhone с и без Dynamic Island
Сроки
2–3 дня для UI-части с локальными обновлениями. Интеграция с сервером для push-обновлений — плюс 1–2 дня. Стоимость рассчитывается индивидуально после анализа требований.
Оценим ваш проект — свяжитесь для консультации. Мы реализовали Live Activity в 15+ проектах, включая крипто-кошельки и трейдинговые терминалы. Гарантируем соблюдение App Store Review Guidelines и оптимизацию энергопотребления. Закажите прототип Live Activity уже сегодня — получите консультацию инженера.







