alpharigel

ios-feedback-button

0
0
# Install this skill:
npx skills add alpharigel/ios-feedback-skill --skill "ios-feedback-button"

Install specific skill from multi-skill repository

# Description

Add a feedback button to an iOS app that creates GitHub issues via a backend API. Use when the user wants to add in-app feedback, bug reporting, or feature request functionality that flows through to GitHub Issues.

# SKILL.md


name: ios-feedback-button
description: Add a feedback button to an iOS app that creates GitHub issues via a backend API. Use when the user wants to add in-app feedback, bug reporting, or feature request functionality that flows through to GitHub Issues.


Add Feedback Button to iOS App (with GitHub Issue Creation)

Add an in-app feedback system that lets users submit feedback from the iOS app, stores it in the backend database, and automatically creates labeled GitHub issues.

Step 0: Understand the Project

Before writing any code, run these commands to understand the project's structure and conventions:

# Detect the GitHub repo (for the GitHub Issues API target)
git remote get-url origin

# Find backend routers/routes
find . -path '*/routers/*.py' -o -path '*/routes/*.py' -o -path '*/endpoints/*.py' 2>/dev/null | head -20

# Find backend services
find . -path '*/services/*.py' -o -path '*/service/*.py' 2>/dev/null | head -20

# Find backend models/schemas
find . -name 'models.py' -o -name 'schemas.py' -o -name 'types.py' 2>/dev/null | grep -v node_modules | grep -v .venv | head -10

# Find database files
find . -name 'database.py' -o -name 'db.py' 2>/dev/null | grep -v node_modules | grep -v .venv | head -10

# Find backend tests
find . -path '*/tests/test_*.py' -o -path '*/test/*.py' 2>/dev/null | grep -v node_modules | grep -v .venv | head -20

# Find iOS views, models, and network layer
find . -path '*/Views/*/*.swift' 2>/dev/null | head -30
find . -path '*/Models/*.swift' 2>/dev/null | head -20
find . -path '*/Network/*.swift' -o -path '*/API/*.swift' -o -path '*/Services/*.swift' 2>/dev/null | head -20

# Find the root/tab view (where the FAB will go)
find . -name 'ContentView.swift' -o -name 'MainView.swift' -o -name 'TabView.swift' -o -name 'RootView.swift' 2>/dev/null | head -5

# Find auth pattern
find . -name 'auth.py' -o -name 'auth.swift' -o -name 'Auth*.swift' 2>/dev/null | grep -v node_modules | grep -v .venv | head -10

Then read the key files from the results above to understand:
- Where to put new files — match existing directory conventions (e.g., if routers are in app/routers/, add feedback.py there)
- How to write models — match the existing model/schema style (Pydantic, SQLAlchemy, etc.)
- How to write the iOS network layer — match the existing API client pattern
- How to register routes — find the main app file and match how other routers are included
- How auth works — use the same auth dependency/middleware pattern for the feedback endpoint

Match the project's existing patterns exactly. Do not write any code until you've read and understood the existing conventions.

Architecture Overview

The feedback system has 4 layers:

iOS UI (FeedbackView) → iOS Network (FeedbackService) → Backend API (POST /feedback) → GitHub Issues API

Key design principles:
- Graceful degradation: GitHub issue creation is best-effort — if it fails, feedback is still saved
- Auto-collected metadata: Device model, OS version, app version are collected automatically on the client
- User enrichment: Backend looks up the authenticated user's name/email to include in the GitHub issue
- Always-visible FAB: Feedback button is a floating action button visible across all tabs

Implementation Steps

Step 1: Backend — Database Table

Add a feedback table to the database schema:

CREATE TABLE IF NOT EXISTS feedback (
    id TEXT PRIMARY KEY,
    user_id TEXT NOT NULL REFERENCES users(id),
    feedback_title TEXT NOT NULL,
    feedback_text TEXT,
    app_version TEXT,
    device_model TEXT,
    os_version TEXT,
    user_name TEXT,
    user_email TEXT,
    github_issue_url TEXT,
    created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now'))
);

Step 2: Backend — Pydantic Models

class FeedbackCreate(BaseModel):
    feedback_title: str
    feedback_text: str | None = None
    app_version: str | None = None
    device_model: str | None = None
    os_version: str | None = None

class FeedbackResponse(BaseModel):
    id: UUID
    user_id: UUID
    feedback_title: str
    feedback_text: str | None
    app_version: str | None
    device_model: str | None
    os_version: str | None
    user_name: str | None
    user_email: str | None
    github_issue_url: str | None
    created_at: datetime

Step 3: Backend — Feedback Service

Create a service module with these functions:

  1. create_feedback(db, user_id, data) — Insert feedback record, return it
  2. create_github_issue(feedback_title, feedback_text, user_name, user_email, metadata) — Call GitHub Issues API:
  3. Read GITHUB_PAT_ISSUES env var (GitHub PAT with issues:write scope)
  4. POST to https://api.github.com/repos/{REPO}/issues
  5. Title format: [Feedback] {feedback_title}
  6. Body includes: user feedback, metadata (device, OS, app version, user info, timestamp)
  7. Labels: ["feedback"]
  8. Return html_url on success, None on failure
  9. Catch all exceptions — never let GitHub failures break feedback submission
  10. update_github_url(db, feedback_id, url) — Update the github_issue_url column after issue creation

Step 4: Backend — API Endpoint

Create POST /feedback endpoint:

@router.post("/feedback", response_model=FeedbackResponse, status_code=201)
async def create_feedback_endpoint(data: FeedbackCreate, user_id=Depends(get_current_user), db=Depends(get_db)):
    # 1. Look up user details (name, email) from users table
    # 2. Save feedback to database
    # 3. Attempt GitHub issue creation (async, non-blocking to user)
    # 4. If issue created, update github_issue_url in DB and response
    # 5. Return feedback record

Register the router in main.py.

Step 5: Backend — Tests

Write tests covering:
- test_create_feedback — minimal feedback (title only) creates record
- test_create_feedback_with_metadata — all metadata fields are persisted
- test_create_feedback_missing_title_rejected — 422 for missing title
- test_github_issue_creation — mock GitHub API, validate issue URL returned
- test_github_issue_failure_still_saves — feedback saved even if GitHub fails

Step 6: iOS — Data Model (DTO)

Create FeedbackDTO.swift:

struct CreateFeedbackDTO: Codable {
    let feedbackTitle: String
    let feedbackText: String?
    let appVersion: String?
    let deviceModel: String?
    let osVersion: String?

    init(title: String, details: String?) {
        self.feedbackTitle = title
        self.feedbackText = details
        // Auto-collect device metadata
        self.appVersion = "\(Bundle.main.infoDictionary?["CFBundleShortVersionString"] ?? "?") (\(Bundle.main.infoDictionary?["CFBundleVersion"] ?? "?"))"
        self.deviceModel = UIDevice.current.model
        self.osVersion = "\(UIDevice.current.systemName) \(UIDevice.current.systemVersion)"
    }
}

struct FeedbackDTO: Codable {
    let id: UUID
    let feedbackTitle: String
    let feedbackText: String?
    let githubIssueUrl: String?
    let createdAt: String
}

Step 7: iOS — Network Service

Create FeedbackService.swift:

struct FeedbackService {
    static func submit(_ dto: CreateFeedbackDTO) async throws -> FeedbackDTO {
        // POST to /feedback with auth header
        // Return decoded FeedbackDTO
    }
}

Step 8: iOS — Feedback View

Create FeedbackView.swift:

  • Form fields:
  • Required: "What would you like to see improved?" (title, single-line TextField)
  • Optional: "Details" (multi-line TextEditor)
  • Submit button: Disabled until title has non-whitespace content; shows loading state
  • Success state: Green checkmark overlay + "Thanks for your feedback!" message, auto-dismiss after ~1.2 seconds
  • Error state: Red error text, re-enable submit button
  • Keyboard: @FocusState to auto-focus title field on appear

Step 9: iOS — Floating Action Button

Add a persistent feedback FAB to the app's root/tab view:

// In the main ContentView or TabView, overlay a floating button
.overlay(alignment: .bottomLeading) {
    Button { showingFeedback = true } label: {
        Image(systemName: "bubble.left.fill")
            .font(.title2)
            .foregroundStyle(.white)
            .frame(width: 50, height: 50)
            .background(.blue)
            .clipShape(Circle())
            .shadow(radius: 4)
    }
    .padding()
}
.sheet(isPresented: $showingFeedback) {
    FeedbackView()
}

The FAB should be visible on all tabs, not just one screen.

Step 10: Environment Setup

  • Set GITHUB_PAT_ISSUES env var on the backend host (e.g., Railway, Heroku, etc.)
  • The PAT needs repo scope (or fine-grained issues:write on the target repo)
  • Create a feedback label in the GitHub repo if it doesn't exist
  • If the env var is missing, feedback still saves — just no GitHub issue created

Checklist

  • [ ] Database: feedback table created with migration
  • [ ] Backend: Pydantic models for create/response
  • [ ] Backend: Feedback service (create, GitHub issue, update URL)
  • [ ] Backend: POST /feedback endpoint with auth
  • [ ] Backend: Tests (5 cases minimum)
  • [ ] iOS: CreateFeedbackDTO with auto-collected metadata
  • [ ] iOS: FeedbackService network layer
  • [ ] iOS: FeedbackView with form, loading, success/error states
  • [ ] iOS: FAB in root view, visible on all tabs
  • [ ] Env: GITHUB_PAT_ISSUES configured on backend host
  • [ ] GitHub: feedback label created in repo

# Supported AI Coding Agents

This skill is compatible with the SKILL.md standard and works with all major AI coding agents:

Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.