Challenges System for Mobile App Engagement
Challenges differ from achievements because they're temporary, often social, and require active participation. "Run 50 km in August" is not "unlocked a badge", it's a commitment with a deadline. It's the urgency and collectivity that create engagement.
Challenge Typology
Solo Challenges — user vs goal. Fixed period, target value. run_50km_august, meditate_20_days. Simplest to implement.
Community Challenges — all users participate together in achieving collective goal. "Let's run 100,000 km in March together". Progress is aggregate of all participants. Motivates through group belonging.
Head-to-Head — two users or teams compete directly. Adds real-time aspect and notifications about competitor progress.
Weekly/Monthly Recurring — repeat on schedule. Creates systematic reason to return to app every week.
Data Model
challenge:
id, title, description
type: ENUM(solo, community, head_to_head, recurring)
metric: VARCHAR -- "workout_count", "distance_km", "streak_days"
target_value: DECIMAL
starts_at, ends_at: TIMESTAMP
xp_reward, badge_id: nullable
is_active: BOOL
user_challenge_participation:
user_id, challenge_id
current_value: DECIMAL -- current progress
joined_at: TIMESTAMP
completed_at: nullable TIMESTAMP
rank: nullable INT -- for competitive
Progress updates event-driven: same workout_completed event that awards XP checks active user challenges and increments current_value. Important — one event handler, not duplicated logic across systems.
Time Limits and Edge Cases
Challenge deadline is the most frequent user complaint source. Typical issues:
"I completed the task but challenge didn't count" — event reached server at 23:59:58, processed at 00:00:01 next day. Use grace period 5 minutes after ends_at.
"I was on a plane without internet" — event saved locally with occurred_at = yesterday, sent today. Backend must accept events with occurred_at in the past (with reasonable limit like 7 days), not by server receipt time.
Double progress credit — on event retransmission from client. Idempotent key (event_id) in each event, unique constraint on (user_id, event_id).
Social Component
Push notifications about community challenge progress: "Our group completed 67% of goal, 3 days left" — send to all participants once daily. Firebase Cloud Messaging with topic messaging: each challenge is a topic, subscribe on join.
Activity feed of participants: "Anna ran 5 km" — adds social pressure. Technically: challenge_activity_feed(user_id, challenge_id, action, value, occurred_at). Client pulls last N entries when opening challenge screen.
How It Looks on Client
List of active and available challenges with cards: progress bar, deadline, participant count. Challenge screen: goal, my progress, activity feed, "Share Progress" button (share card screenshot via UIActivityViewController).
On completion — celebration overlay with animation, XP/badge award, CTA "Join next challenge".
Timeline Guidelines
Solo challenges with progress and notifications — 3–5 days (client + backend). Community and head-to-head with activity feed, real-time updates and social notifications — 2–3 weeks. Pricing is calculated individually.







