Showing Feedback UI
Option 1: Modal Presentation (Recommended)
Copy
struct ContentView: View {
@State private var showFeedback = false
var body: some View {
NavigationView {
VStack {
Button("Send Feedback") {
showFeedback = true
}
.padding()
}
.navigationTitle("My App")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("Feedback") {
showFeedback = true
}
}
}
}
.sheet(isPresented: $showFeedback) {
SuggestKitView()
}
}
}
Option 2: Navigation Push
Copy
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
NavigationLink("Send Feedback") {
SuggestKitView()
}
.padding()
}
.navigationTitle("My App")
}
}
}
Option 3: Full Screen
Copy
struct ContentView: View {
@State private var showFeedback = false
var body: some View {
VStack {
Button("Send Feedback") {
showFeedback = true
}
}
.fullScreenCover(isPresented: $showFeedback) {
SuggestKitView()
}
}
}
Pre-filled Content
Pre-populate the feedback form:Copy
SuggestKitView(
initialTitle: "Feature Request",
initialDescription: "I would like to see...",
initialCategory: "Feature Request"
)
Handling Events
Listen to user actions and suggestion events:Copy
SuggestKitView()
.onSuggestionSubmitted { suggestion in
print("User submitted: \(suggestion.title)")
// Track analytics, show thank you message, etc.
}
.onSuggestionVoted { suggestion, voted in
print("User \(voted ? "voted for" : "unvoted") \(suggestion.title)")
}
.onDismiss {
print("Feedback view dismissed")
// Track engagement metrics
}
.onError { error in
print("Error occurred: \(error.localizedDescription)")
// Handle errors gracefully
}
Custom Categories
Define specific feedback categories:Copy
SuggestKitView()
.categories([
"Bug Report",
"Feature Request",
"UI/UX Improvement",
"Performance Issue",
"Documentation"
])
Feedback Button Component
Create a reusable feedback button:Copy
struct FeedbackButton: View {
@State private var showFeedback = false
let title: String
let icon: String
init(title: String = "Send Feedback", icon: String = "lightbulb") {
self.title = title
self.icon = icon
}
var body: some View {
Button(action: {
showFeedback = true
}) {
HStack {
Image(systemName: icon)
Text(title)
}
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(8)
}
.sheet(isPresented: $showFeedback) {
SuggestKitView()
}
}
}
// Usage
FeedbackButton()
FeedbackButton(title: "Suggest Feature", icon: "plus.circle")
Inline Suggestions List
Display suggestions directly in your app:Copy
struct SuggestionsList: View {
@StateObject private var suggestions = SuggestKitSuggestions()
var body: some View {
List(suggestions.items) { suggestion in
SuggestionRow(suggestion: suggestion)
}
.onAppear {
suggestions.load()
}
.refreshable {
await suggestions.refresh()
}
}
}
struct SuggestionRow: View {
@ObservedObject var suggestion: Suggestion
var body: some View {
VStack(alignment: .leading, spacing: 8) {
Text(suggestion.title)
.font(.headline)
Text(suggestion.description)
.font(.body)
.foregroundColor(.secondary)
.lineLimit(3)
HStack {
VoteButton(suggestion: suggestion)
Spacer()
StatusBadge(status: suggestion.status)
}
}
.padding(.vertical, 4)
}
}
Vote Button Component
Copy
struct VoteButton: View {
@ObservedObject var suggestion: Suggestion
var body: some View {
Button(action: {
Task {
if suggestion.hasVoted {
await suggestion.removeVote()
} else {
await suggestion.vote()
}
}
}) {
HStack(spacing: 4) {
Image(systemName: suggestion.hasVoted ? "heart.fill" : "heart")
Text("\(suggestion.voteCount)")
}
.foregroundColor(suggestion.hasVoted ? .red : .gray)
.font(.caption)
}
.disabled(suggestion.isLoading)
}
}
Status Badge Component
Copy
struct StatusBadge: View {
let status: SuggestionStatus
var body: some View {
Text(status.displayName)
.font(.caption)
.padding(.horizontal, 8)
.padding(.vertical, 4)
.background(status.color)
.foregroundColor(.white)
.cornerRadius(4)
}
}
extension SuggestionStatus {
var displayName: String {
switch self {
case .pending: return "Pending"
case .inProgress: return "In Progress"
case .completed: return "Completed"
case .rejected: return "Rejected"
}
}
var color: Color {
switch self {
case .pending: return .orange
case .inProgress: return .blue
case .completed: return .green
case .rejected: return .red
}
}
}
Contextual Triggers
Show feedback at appropriate moments:Copy
struct OnboardingView: View {
@State private var currentStep = 0
@State private var showFeedback = false
var body: some View {
VStack {
// Onboarding content...
if currentStep == 3 {
Button("How was your experience?") {
showFeedback = true
}
.padding()
}
}
.sheet(isPresented: $showFeedback) {
SuggestKitView(
initialTitle: "Onboarding Feedback",
initialDescription: "How can we improve the onboarding experience?"
)
}
}
}
Error Handling
Handle network and data errors gracefully:Copy
struct ContentView: View {
@State private var showFeedback = false
@State private var showError = false
@State private var errorMessage = ""
var body: some View {
Button("Send Feedback") {
showFeedback = true
}
.sheet(isPresented: $showFeedback) {
SuggestKitView()
.onError { error in
errorMessage = error.localizedDescription
showError = true
showFeedback = false
}
}
.alert("Error", isPresented: $showError) {
Button("OK") { }
} message: {
Text(errorMessage)
}
}
}