Реалізація системи досягнень та бейджів для геймификації мобільного додатка
Система досягнень впливає на retention не тому що користувачам подобаються іконки. Вона працює через completion psychology: незавершений прогрессбар викликає бажання його закрити. Досягнення, яке видно але недосяжне — дратує. Видно та легко досяжне — залучає. Це інженерна задача з психологічним контекстом.
Модель даних
Три ключові сутності:
Achievement — шаблон досягнення: id, title, description, icon_url, criteria (тип події + поріг), xp_reward, badge_type (bronze/silver/gold/platinum). Критерії зберігаємо як JSON: {"event":"workout_completed","count":10} або {"event":"streak_days","count":7}.
UserAchievement — факт отримання: user_id, achievement_id, progress (поточне значення), unlocked_at (null якщо не розблоковано).
AchievementEvent — лог подій для пересчета прогресу: user_id, event_type, value, occurred_at. Це важливо — пересчет прогресу повинен бути ідемпотентним та відновлюваним з логу.
Логіка перевірки та розблокування
Два підходи: event-driven (на кожну подію перевіряємо все релевантні досягнення) та batch (періодичний пересчет всіх користувачів). Для більшості додатків — event-driven на бекенді.
Клієнт відправляє подію (наприклад workout_completed) → бекенд incrementы лічильник у UserAchievement.progress → якщо progress >= criteria.count та unlocked_at IS NULL → встановлюємо unlocked_at, записуємо XP, відправляємо push через APNs/FCM.
Критично: операція повинна бути атомарною. Конкурентні запити від одного користувача з двох пристроїв не повинні дважди розблокувати досягнення. Postgres: UPDATE з WHERE unlocked_at IS NULL RETURNING * у транзакції.
Відображення на клієнті
Три стани бейджа: locked (з прогрессбаром), unlocked (повнокольоровий), newly unlocked (анімація розблокування).
Анімація розблокування — момент, який неможна зіпсувати. На iOS: UIViewPropertyAnimator + Lottie для складних анімацій. На Flutter: AnimationController + Lottie widget. Файл анімації (.json Lottie) для кожного типу бейджа — bronze/silver/gold окремо. Не роби це через GIF — якість гірша, контролю менше.
Тост або full-screen момент? Залежить від ваги досягнення. Звичайне досягнення — bottom sheet або overlay на 2–3 секунди. Рідкісне (gold/platinum) — full-screen celebration з конфеті (Confetti package на Flutter, kastomний CAEmitterLayer на iOS).
Прогрес та вітрина
Екран «Мої досягнення»: групування за категоріями, locked досягнення видні з прогрессбаром (motivational), unlocked — з датою. Не скривай locked досягнення — це вбиває motivation loop.
Лічильник розблокованих досягнень в профілі користувача, total XP від досягнень (якщо є система рівнів). Шейринг бейджа в соцмереж — UIActivityViewController з kastомним превю (UIActivityItemProvider).
Орієнтири за термінами
Базова система з 20–30 досягненнями, event-driven бекендом та анімацією розблокування — 2–3 дні для клієнтської частини при готовому бекенді, 1–2 тижні з бекендом. Розширена з kastомними Lottie-анімаціями, категоріями, шерингом та аналітикою розблокувань — 3–4 тижні. Вартість розраховується індивідуально.







