Implementing Personalized Onboarding Based on Questionnaire in Mobile Apps
Duolingo asks "Why are you learning a language?" and shows different content by answer. Headspace — "What's bothering you?" and offers matching meditations. This isn't just UX pattern — it's mechanism significantly increasing user return probability: app is immediately relevant to them.
Questionnaire Architecture
Personalization questionnaire isn't just screens with "Next" buttons. It's flow with branching: answer to question 2 may determine what question 3 is. And result — not just saved answers, but set of parameters affecting app's initial state.
Data-driven configuration. Each question — object with metadata:
struct OnboardingQuestion {
let id: String
let type: QuestionType // .singleChoice, .multiChoice, .slider, .text
let title: String
let subtitle: String?
let options: [QuestionOption]
let nextQuestionId: [String: String] // optionId -> nextQuestionId, or "default"
let analyticsKey: String
}
Branching logic via nextQuestionId dictionary: if user selected "Stress reduction" — next question q_stress_level, if "Better sleep" — q_sleep_problems. "default" — for questions without branching. Config loaded from server or bundled as JSON file.
QuestionnaireCoordinator manages question stack: history: [String] for "Back" button support, currentQuestionId: String, answers: [String: Any] for accumulating responses. On "Back" press — history.popLast() and return to previous question with saved answer.
Questionnaire UI
Navigation Between Questions
Transition between question screens — horizontal animation. On iOS: UIPageViewController in .scroll style or UIView.transition(with:duration:options:animations:) with .transitionCurlUp for unusual feel. In SwiftUI — TabView with tabViewStyle(.page) with fixed selection doesn't auto-swipe back — control via interactiveDismissDisabled.
Best option — custom animation via withAnimation(.spring(response: 0.4, dampingFraction: 0.8)) with offset and opacity. Full control: forward — slide left, backward — slide right.
Question Types
Single choice — card or item list, tap immediately goes to next (no "Next" button). Haptic .selection on select. Selected card — animated background and border change.
Multi choice — checkboxes or cards with multiple selection. "Next" button appears after selecting at least one. LazyVGrid / FlowLayout for cards with tags.
Slider — UISlider / Slider in SwiftUI for numbers (age, frequency, level). Labels on edges for context. Haptic .selection at multiples.
Progress Indicator
Progress bar shows position in questionnaire. Animated LinearProgressView / LinearProgressIndicator. Don't show exact step ("3 of 7") — user sees end and may quit. Show only bar without numbers.
Personalizing Results
After completing questionnaire build UserProfile from answers:
struct UserProfile {
let primaryGoal: Goal // from q_goal question
let experienceLevel: Level // from q_experience question
let preferredTopics: [Topic] // from q_topics (multi)
let availableTime: Int // minutes per day from q_time question
}
This profile determines: what content shown on main screen, what push notifications sent, which sections hidden/shown, what complexity level user starts with. Save locally (UserDefaults / DataStore) and sync server — on reinstall or device change user doesn't repeat questionnaire.
Skipping and Editing
"Skip" button — always. Not everyone wants to tell about themselves. For skippers show default content and periodically (3–7 days) softly offer filling "for better recommendations".
Ability to change answers later — in profile settings. Don't lock user in initial choice. OnboardingQuestionnaireView reused in settings with edit mode — same components, but pre-filled answers and different completion-action (profile update instead of main screen).
Analytics. Firebase Analytics: questionnaire_started, questionnaire_step_completed (with step_id and time_spent), questionnaire_skipped (with at_step), questionnaire_completed. From this data see — where users drop, which answers most popular, how questionnaire affects retention.
Timeline: 2–3 days. Linear questionnaire from 5–7 questions with simple choice and result application to initial content — 2 days. Questionnaire with branching, server-driven config, multiple question types, analytics and editing in profile — 3 days.







