Apps that actively collect and act on feedback have 2x better retention and 4.5+ star ratings. This guide covers building a comprehensive feedback system.
Why Feedback Matters
- Identify bugs before they hurt ratings
- Discover feature requests from power users
- Measure satisfaction (NPS, CSAT)
- Reduce churn with proactive support
- Guide product roadmap
- Build user loyalty (they feel heard)
Types of Feedback
Solicited Feedback
Proactive collection:
- In-app surveys
- NPS surveys
- Feature satisfaction ratings
- Email surveys
- User interviews
- Beta tester feedback
Advantages:
✓ Targeted questions
✓ Structured data
✓ Measurable metrics
Unsolicited Feedback
User-initiated:
- App Store reviews
- Support tickets
- Social media mentions
- Community forums
Advantages:
✓ Authentic opinions
✓ Identifies pain points
✓ Shows sentiment
In-App Feedback Widget
iOS Implementation
import UIKit
class FeedbackViewController: UIViewController {
private let feedbackTextView = UITextView()
private let categoryPicker = UISegmentedControl(items: ["Bug", "Feature", "Other"])
private let emailField = UITextField()
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
}
private func setupUI() {
title = "Send Feedback"
// Category
categoryPicker.selectedSegmentIndex = 0
// Text area
feedbackTextView.layer.borderColor = UIColor.systemGray4.cgColor
feedbackTextView.layer.borderWidth = 1
feedbackTextView.layer.cornerRadius = 8
feedbackTextView.font = .systemFont(ofSize: 16)
// Email
emailField.placeholder = "Your email (optional)"
emailField.borderStyle = .roundedRect
emailField.keyboardType = .emailAddress
// Submit button
let submitButton = UIButton(type: .system)
submitButton.setTitle("Submit Feedback", for: .normal)
submitButton.addTarget(self, action: #selector(submitFeedback), for: .touchUpInside)
// Layout
let stackView = UIStackView(arrangedSubviews: [
categoryPicker, feedbackTextView, emailField, submitButton
])
stackView.axis = .vertical
stackView.spacing = 16
stackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackView)
NSLayoutConstraint.activate([
stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
feedbackTextView.heightAnchor.constraint(equalToConstant: 150)
])
}
@objc private func submitFeedback() {
guard !feedbackTextView.text.isEmpty else {
showAlert(title: "Error", message: "Please enter your feedback")
return
}
let category = ["Bug", "Feature", "Other"][categoryPicker.selectedSegmentIndex]
let feedback = Feedback(
category: category,
message: feedbackTextView.text,
email: emailField.text,
appVersion: Bundle.main.appVersion,
osVersion: UIDevice.current.systemVersion,
deviceModel: UIDevice.current.model,
timestamp: Date()
)
FeedbackService.shared.submit(feedback) { [weak self] success in
DispatchQueue.main.async {
if success {
self?.showAlert(title: "Thank You!", message: "Your feedback has been submitted") {
self?.dismiss(animated: true)
}
} else {
self?.showAlert(title: "Error", message: "Failed to submit feedback")
}
}
}
}
}
struct Feedback: Codable {
let category: String
let message: String
let email: String?
let appVersion: String
let osVersion: String
let deviceModel: String
let timestamp: Date
}
class FeedbackService {
static let shared = FeedbackService()
func submit(_ feedback: Feedback, completion: @escaping (Bool) -> Void) {
// Send to your backend API
guard let url = URL(string: "https://api.yourapp.com/feedback") else {
completion(false)
return
}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try? JSONEncoder().encode(feedback)
URLSession.shared.dataTask(with: request) { _, response, error in
completion(error == nil && (response as? HTTPURLResponse)?.statusCode == 200)
}.resume()
}
}
Android Implementation
class FeedbackActivity : AppCompatActivity() {
private lateinit var binding: ActivityFeedbackBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityFeedbackBinding.inflate(layoutInflater)
setContentView(binding.root)
setupUI()
}
private fun setupUI() {
binding.submitButton.setOnClickListener {
submitFeedback()
}
}
private fun submitFeedback() {
val message = binding.feedbackEditText.text.toString()
if (message.isEmpty()) {
Toast.makeText(this, "Please enter your feedback", Toast.LENGTH_SHORT).show()
return
}
val category = when (binding.categoryGroup.checkedRadioButtonId) {
R.id.bugRadio -> "Bug"
R.id.featureRadio -> "Feature"
else -> "Other"
}
val feedback = Feedback(
category = category,
message = message,
email = binding.emailEditText.text.toString(),
appVersion = BuildConfig.VERSION_NAME,
osVersion = Build.VERSION.RELEASE,
deviceModel = Build.MODEL,
timestamp = System.currentTimeMillis()
)
lifecycleScope.launch {
val success = FeedbackRepository.submitFeedback(feedback)
if (success) {
Toast.makeText(this@FeedbackActivity, "Thank you for your feedback!", Toast.LENGTH_SHORT).show()
finish()
} else {
Toast.makeText(this@FeedbackActivity, "Failed to submit feedback", Toast.LENGTH_SHORT).show()
}
}
}
}
data class Feedback(
val category: String,
val message: String,
val email: String,
val appVersion: String,
val osVersion: String,
val deviceModel: String,
val timestamp: Long
)
NPS (Net Promoter Score)
What is NPS?
Question: "How likely are you to recommend this app to a friend?"
Scale: 0-10
Scoring:
- Promoters (9-10): Loyal enthusiasts
- Passives (7-8): Satisfied but unenthusiastic
- Detractors (0-6): Unhappy users
NPS = % Promoters - % Detractors
Example:
60% Promoters, 10% Detractors = NPS 50
Benchmarks:
- Excellent: 50+
- Good: 30-50
- Acceptable: 0-30
- Poor: < 0
NPS Implementation
iOS:
class NPSViewController: UIViewController {
private let question = UILabel()
private let scoreButtons: [UIButton] = (0...10).map { score in
let button = UIButton(type: .system)
button.setTitle("(score)", for: .normal)
button.tag = score
return button
}
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
}
private func setupUI() {
question.text = "How likely are you to recommend our app?"
question.numberOfLines = 0
question.textAlignment = .center
let stackView = UIStackView(arrangedSubviews: scoreButtons)
stackView.axis = .horizontal
stackView.distribution = .fillEqually
stackView.spacing = 8
scoreButtons.forEach { button in
button.addTarget(self, action: #selector(scoreSelected), for: .touchUpInside)
}
// Layout...
}
@objc private func scoreSelected(_ sender: UIButton) {
let score = sender.tag
// Save response
AnalyticsService.logNPS(score: score)
// Follow-up question
if score >= 9 {
// Promoter: Ask for review
requestAppStoreReview()
} else if score <= 6 {
// Detractor: Ask why
showFollowUpQuestion("What could we improve?")
} else {
// Passive: Thank and dismiss
showThankYou()
}
}
}
When to show:
- After 7 days of use
- After positive action (achievement)
- Not more than once per 3 months
- Not if user recently left review
Feature Satisfaction Surveys
Micro-Surveys
Context-aware surveys:
After using feature:
"How was your experience with [Feature]?"
😞 😐 🙂 😀
After completing action:
"Did this meet your expectations?"
👍 👎
In-app prompt:
"Quick question: Is [Feature] useful?"
[Yes] [No] [Not sure]
Keep it:
✓ 1-2 questions max
✓ At relevant moment
✓ Easy to dismiss
✓ Not intrusive
CSAT (Customer Satisfaction)
Question: "How satisfied are you with [Feature]?"
Scale: 1-5 stars
Show after:
- Support interaction
- Feature usage
- Purchase completion
CSAT Score = (Positive responses / Total responses) × 100
Target: 80%+ satisfaction
App Store Review Management
Review Monitoring
Tools:
- App Store Connect API
- Google Play Developer API
- AppFollow ($29+/month)
- App Annie (free tier)
Monitor:
- New reviews daily
- Rating changes
- Review sentiment
- Common keywords/complaints
Review Response Strategy
Response guidelines:
1. Respond quickly (< 24 hours)
2. Thank user for feedback
3. Address specific issues
4. Offer solution/help
5. Professional tone
6. No defensive responses
Positive review (5 stars):
"Thank you so much for the kind words! We're thrilled you're enjoying the app. 🎉"
Negative review (1-2 stars):
"We're sorry to hear about your experience. We'd love to help make this right.
Please contact us at [email protected] with more details so we can assist you.
[Issue mentioned] has been fixed in our latest update."
Bug report:
"Thank you for reporting this! We've identified the issue and a fix will be
released in version 2.1 next week. We appreciate your patience!"
Feature request:
"Great suggestion! We've added this to our roadmap and will keep you updated.
Join our community at [link] to vote on features."
Automated Review Requests
iOS - SKStoreReviewController:
import StoreKit
func requestReviewIfAppropriate() {
// Timing logic
let launchCount = UserDefaults.standard.integer(forKey: "launchCount")
let lastReviewRequest = UserDefaults.standard.double(forKey: "lastReviewRequest")
let daysSinceLastRequest = (Date().timeIntervalSince1970 - lastReviewRequest) / 86400
// Only ask if:
// - Used app 10+ times
// - Haven't asked in 120 days
// - User has completed positive action
if launchCount >= 10 && daysSinceLastRequest >= 120 {
if let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene {
SKStoreReviewController.requestReview(in: scene)
UserDefaults.standard.set(Date().timeIntervalSince1970, forKey: "lastReviewRequest")
}
}
}
// Trigger after positive moments:
- Completed first task successfully
- Reached milestone
- Positive feedback given
- Achievement unlocked
Feature Request System
Public Roadmap
Tools:
- Canny ($79+/month)
- ProductBoard ($20+/user/month)
- Trello (free)
- GitHub Discussions (free)
Features:
✓ Users submit ideas
✓ Upvoting system
✓ Status tracking (Planned, In Progress, Shipped)
✓ Comments/discussion
✓ Admin moderation
Benefits:
- Transparent development
- User engagement
- Prioritization data
- Reduced support tickets (users see it's planned)
Feature Voting
Implementation:
In-app "Request Feature" button
→ Opens feature request form
→ Submit to backend
→ Display in roadmap
→ Users can upvote
Prioritization:
Priority Score = (Upvotes × Weight) + (Team Score)
Where:
- Upvotes: Community interest
- Weight: User segment (paid users = 3x, free = 1x)
- Team Score: Strategic importance (0-10)
User Interviews
Recruiting Interviewees
In-app recruitment:
"Help us improve! Get 1 month free premium for a 30-minute call."
Criteria:
- Power users (high engagement)
- Recent churned users (why they left)
- Target persona
- Diverse user segments
Incentives:
- Premium subscription
- Gift cards ($25-100)
- Early access to features
- Swag
Interview Questions
User behavior:
1. Walk me through how you use the app
2. What problem does it solve for you?
3. How often do you use it?
4. What alternatives have you tried?
Pain points:
5. What frustrates you most?
6. What feature do you wish existed?
7. What almost made you uninstall?
Value:
8. What do you love about the app?
9. Would you recommend it? Why/why not?
10. What would make it a 5-star app?
Document:
- Record (with permission)
- Take notes
- Identify patterns across interviews
Feedback Analysis
Categorization
Tag feedback:
- Bug reports
- Feature requests
- UX improvements
- Performance issues
- Pricing feedback
- Support issues
- Praise
Priority levels:
- P0: Critical (app broken)
- P1: Important (affects many users)
- P2: Nice to have
- P3: Future consideration
Sentiment Analysis
Automated sentiment:
- Positive: 😀 (4-5 stars)
- Neutral: 😐 (3 stars)
- Negative: 😞 (1-2 stars)
Tools:
- MonkeyLearn API
- Google Cloud Natural Language
- Custom ML model
Track sentiment over time:
Week 1: 70% positive
Week 2: 65% positive → Investigate drop
Week 3: 75% positive → Improvement working
Feedback Dashboard
Metrics to track:
- Total feedback volume
- Sentiment breakdown
- Top issues (by frequency)
- Feature request votes
- NPS score trend
- Review rating average
- Response rate
- Time to resolution
Tools:
- Tableau/Looker (data viz)
- Custom admin panel
- Notion database
Acting on Feedback
Closing the Loop
When you fix something:
1. Update user who reported it
Email: "Good news! The [issue] you reported has been fixed in v2.3."
2. Post public update
"Based on your feedback, we've added [feature]!"
3. Mention in release notes
"Fixed: [Bug] as reported by users"
4. Thank contributors
In-app: "Special thanks to [users] for feedback"
Shows users their voice matters → Increased engagement
Product Roadmap Integration
Feedback → Roadmap flow:
1. Collect feedback → Database
2. Categorize and tag → Feature requests
3. Aggregate and prioritize → Top 10 requests
4. Evaluate feasibility → Engineering estimate
5. Add to roadmap → Q2 2025 target
6. Build and ship → Release v2.5
7. Announce to users → Email campaign
8. Measure impact → Retention +15%
Repeat monthly
Support Integration
Help Center
Self-service support:
- FAQ section
- Video tutorials
- Search functionality
- Troubleshooting guides
- Contact form
Tools:
- Zendesk ($19+/agent/month)
- Intercom ($74+/month)
- Help Scout ($20+/user/month)
- Notion (DIY, free)
Live Chat
In-app chat widget:
- Real-time messaging
- Canned responses
- Ticket creation
- User context (app version, device)
Tools:
- Intercom
- Zendesk Chat
- Drift
- Crisp
Response time:
- Target: < 2 hours
- Excellent: < 30 minutes
Feedback Metrics
Response Rate
Survey Response Rate:
= Responses / Surveys Shown × 100
Target: 5-15%
Improve with:
- Shorter surveys (1-2 questions)
- Better timing (after positive action)
- Incentives (chance to win gift card)
Resolution Time
Average Resolution Time:
= Total time to resolve / Number of issues
Target:
- P0: < 24 hours
- P1: < 3 days
- P2: < 2 weeks
Track: Issue creation date → Closed date
Privacy Considerations
Data Collection
Collect with consent:
- Email (optional)
- Device info (for bug context)
- App version
- Feedback text
Don't collect:
- Personal info (unless volunteered)
- Location
- Sensitive data
Privacy policy:
- State what data is collected
- How it's used
- Retention period
- User rights
Feedback Best Practices
Dos
- ✅ Make feedback easy (1 tap away)
- ✅ Respond to all feedback
- ✅ Act on common requests
- ✅ Close the loop with users
- ✅ Monitor trends over time
- ✅ Thank users for feedback
Don'ts
- ❌ Ignore negative feedback
- ❌ Survey too frequently (fatigue)
- ❌ Make surveys too long (> 3 questions)
- ❌ Defensive responses to reviews
- ❌ Collect feedback without acting
- ❌ Hide feedback channels
Feedback Tools Comparison
Surveying
- Typeform: Beautiful surveys ($25+/month)
- SurveyMonkey: Full-featured ($25+/month)
- Google Forms: Simple, free
- Qualtrics: Enterprise ($1,500+/year)
In-App Feedback
- Instabug: Comprehensive ($49+/month)
- Apptentive: Mobile-focused (custom pricing)
- UserVoice: Feature requests ($499+/month)
- Custom build: Full control, DIY
Conclusion
A robust feedback system is essential for product success. Collect feedback through multiple channels, analyze for patterns, prioritize based on impact, act quickly on critical issues, and always close the loop with users. Users who feel heard become your biggest advocates.