Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Generates an in-app feedback collection form with category selection, text input, optional screenshot attachment, device diagnostics, and smart routing — directing happy users to App Store reviews and unhappy users to support. Use when user wants feedback, bug reports, feature requests, or contact support forms.
.claude/skills/rshankras-feedback-form/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-07 | ✗→✓ | ▲ Improved | 58% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 58% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 26% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 337% | 0% |
| case-15 | ✗→✓ | ▲ Improved | 24% | 0% |
Generate a production in-app feedback form with category selection, sentiment-based rating, optional screenshot attachment, device diagnostics collection, and smart routing that funnels satisfied users to the App Store review prompt and dissatisfied users to a support channel.
Use this skill when the user:
Search for existing feedback or support code:
Glob: **/*Feedback*.swift, **/*Support*.swift, **/*BugReport*.swift, **/*ContactForm*.swift
Grep: "MFMailComposeViewController" or "FeedbackForm" or "SKStoreReviewController"If third-party feedback SDK found (Instabug, UserVoice, Zendesk):
Check for MessageUI availability:
Grep: "import MessageUI" or "MFMailCompose"Note: MessageUI is iOS-only. macOS uses NSSharingService or direct webhook delivery.
Ask user via AskUserQuestion:
Read templates.md for production Swift code.
Generate these files:
FeedbackCategory.swift — Enum with SF Symbol icons and display namesFeedbackEntry.swift — Data model for a feedback submissionDeviceDiagnostics.swift — Collects device and app infoFeedbackFormView.swift — SwiftUI form with sentiment, category, message, screenshotsFeedbackSubmitter.swift — Protocol + EmailFeedbackSubmitter + WebhookFeedbackSubmitterBased on configuration:
ScreenshotCapture.swift — If screenshot capture selectedCheck project structure:
Sources/ exists -> Sources/Feedback/App/ exists -> App/Feedback/Feedback/After generation, provide:
Feedback/
├── FeedbackCategory.swift # Category enum with icons
├── FeedbackEntry.swift # Feedback data model
├── DeviceDiagnostics.swift # Device info collector
├── FeedbackFormView.swift # SwiftUI form view
├── FeedbackSubmitter.swift # Email + webhook delivery
└── ScreenshotCapture.swift # Screen capture (optional)Present the feedback form from any view:
swift@State private var showFeedback = false Button("Send Feedback") { showFeedback = true } .sheet(isPresented: $showFeedback) { FeedbackFormView() }
In a settings screen:
swiftForm { Section("Support") { Button { showFeedback = true } label: { Label("Send Feedback", systemImage: "bubble.left.and.text.bubble.right") } } } .sheet(isPresented: $showFeedback) { FeedbackFormView() }
With a pre-selected category (e.g., from a help menu):
swiftFeedbackFormView(initialCategory: .bugReport)
swift@Test func feedbackEntryEncodesCorrectly() throws { let entry = FeedbackEntry( category: .bugReport, message: "App crashes when tapping save", rating: 2, screenshots: [], deviceInfo: DeviceDiagnostics.collect(), appVersion: "1.2.3", timestamp: Date() ) let data = try JSONEncoder().encode(entry) let decoded = try JSONDecoder().decode(FeedbackEntry.self, from: data) #expect(decoded.category == .bugReport) #expect(decoded.rating == 2) } @Test func webhookSubmitterSendsCorrectPayload() async throws { let mockSession = MockURLSession() let submitter = WebhookFeedbackSubmitter( url: URL(string: "https://example.com/feedback")!, session: mockSession ) let entry = FeedbackEntry( category: .featureRequest, message: "Dark mode support please", rating: 4, screenshots: [], deviceInfo: DeviceDiagnostics.collect(), appVersion: "1.0.0", timestamp: Date() ) try await submitter.submit(entry) #expect(mockSession.lastRequest?.httpMethod == "POST") #expect(mockSession.lastRequest?.value(forHTTPHeaderField: "Content-Type") == "application/json") } @Test func sentimentRoutingDirectsHighRatingToReview() { let entry = FeedbackEntry( category: .praise, message: "Love this app!", rating: 5, screenshots: [], deviceInfo: DeviceDiagnostics.collect(), appVersion: "1.0.0", timestamp: Date() ) #expect(entry.suggestsAppStoreReview) // rating >= 4 }
swiftButton { showFeedback = true NSApp.activate(ignoringOtherApps: true) } label: { Label("Send Feedback", systemImage: "envelope") } .sheet(isPresented: $showFeedback) { FeedbackFormView() }
swiftlet screenshot = try await ScreenshotCapture.captureCurrentWindow() let entry = FeedbackEntry( category: .bugReport, message: "Layout is broken on this screen", rating: 1, screenshots: [screenshot], deviceInfo: DeviceDiagnostics.collect(), appVersion: Bundle.main.appVersion, timestamp: Date() ) try await submitter.submit(entry)
swiftif entry.suggestsAppStoreReview { // Happy user — ask for App Store review if let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene { SKStoreReviewController.requestReview(in: scene) } } else if entry.suggestsSupportFollowUp { // Unhappy user — show support confirmation showSupportConfirmation = true }
MFMailComposeViewController.canSendMail() before presenting. Fall back to webhook or mailto: URL if unavailable.UITextField.isSecureTextEntry areas as a guide.NWPathMonitor to detect network changes.SKStoreReviewController.requestReview() is rate-limited by the system (typically 3 times per 365-day period). Don't rely on it always appearing.NSSharingService(named: .composeEmail) or prefer webhook delivery on macOS.generators/review-prompt — App Store review prompt timing and strategyOther measured skills in the registry, with their headline benchmark lift.