MCPInstall

ios-marketing-capture-automation

Automate multi-locale marketing screenshot capture for SwiftUI iOS apps with in-app capture system, demo data seeding, and element rendering.

Installs
1K
Last 24h
—
Trending rank
—
Security
Not audited

Install

Works with every agent the skills CLI supports.

npx skills add https://github.com/reason-machines/marketing-skills --skill ios-marketing-capture-automation
  1. 01

    Install

    Run the command in your project. The CLI asks which agents to install for.

  2. 02

    Describe the task

    Ask your agent for something this skill covers. It loads ios-marketing-capture-automation automatically when relevant.

  3. 03

    Review the result

    The agent follows the SKILL.md steps, scripts and references bundled with the skill.

SKILL.md

The instructions your agent reads when the skill is triggered.

iOS Marketing Capture Automation

Skill by ara.so — Marketing Skills collection.

What This Skill Does

This skill helps you automate marketing screenshot capture for SwiftUI iOS apps by building an in-app capture system that:

  • Adds a #if DEBUG-gated capture system with zero production footprint
  • Seeds deterministic demo data so screenshots look populated and polished
  • Navigates to each screen programmatically via step-based coordinator
  • Snapshots full window including status bar, safe area, and presented sheets
  • Renders isolated elements (cards, widgets, charts) via ImageRenderer at 3x with transparency
  • Loops every locale automatically — one build, N relaunches with -AppleLanguages
  • Works with any SwiftUI navigation: TabView, NavigationStack, NavigationSplitView

Installation

Using npx skills (recommended)

npx skills add ParthJadhav/ios-marketing-capture

Global install (available across all projects):

npx skills add ParthJadhav/ios-marketing-capture -g

Agent-specific install:

npx skills add ParthJadhav/ios-marketing-capture -a claude-code

Manual installation

git clone https://github.com/ParthJadhav/ios-marketing-capture ~/.claude/skills/ios-marketing-capture

Requirements Checklist

Before starting, verify:

  • ✅ Xcode 16+ (synchronized folder groups support)
  • ✅ iOS 17+ deployment target (for ImageRenderer, @Observable)
  • ✅ A simulator runtime matching target iOS version
  • ✅ Python 3 (for JSON parsing in shell script)
  • ✅ SwiftUI-based app with defined navigation structure

Core Concepts

In-App Capture (Not XCUITest)

This approach uses in-app capture instead of XCUITest/Fastlane because:

  • No test target needed — many projects lack one, adding means fragile pbxproj edits
  • Direct access — ViewModels, SwiftData, ImageRenderer, UIWindow.drawHierarchy
  • Faster — xcodebuild build once, then simctl launch per locale
  • Element renders require it — ImageRenderer must run inside app process

Step-Based Coordinator

Each screenshot is a self-contained CaptureStep:

struct CaptureStep {
    let name: String                         // "01-home"
    let navigate: @MainActor () -> Void      // put app in right state
    let settle: Duration                     // wait for animations
    let cleanup: (@MainActor () -> Void)?    // tear down before next step
}

Implementation Pattern

1. Gather Requirements

When user asks to capture screenshots, collect:

  1. Screens to capture — exact tab names or navigation paths
  2. Elements to render — cards, widgets, charts to isolate
  3. Locales — explicit list or "all locales in xcstrings"
  4. Device — simulator model (e.g. "iPhone 17")
  5. Appearance — light, dark, or both
  6. Seed data requirements — what demo data needs to populate

2. Generated File Structure

Create this structure:

YourApp/
├── Debug/
│   └── MarketingCapture.swift      # Capture system (DEBUG-only)
├── ContentView.swift               # Modified — DEBUG hook
scripts/
└── capture-marketing.sh            # Build + locale loop

Output lands in:

marketing/
    en/
        01-home.png
        02-detail.png
        elements/
            card-item.png
            widget-small.png
    de/
        01-home.png
        ...

3. Core Capture System Code

#if DEBUG
import SwiftUI

struct CaptureStep {
    let name: String
    let navigate: @MainActor () -> Void
    let settle: Duration
    let cleanup: (@MainActor () -> Void)?
}

@Observable
final class MarketingCaptureCoordinator {
    var isCapturing = false
    var currentStep = 0
    
    private let steps: [CaptureStep]
    private let outputDir: URL
    private let elementsDir: URL
    
    init(steps: [CaptureStep], locale: String) {
        self.steps = steps
        
        let base = FileManager.default
            .urls(for: .documentDirectory, in: .userDomainMask)[0]
            .deletingLastPathComponent()
            .deletingLastPathComponent()
            .appendingPathComponent("marketing/\(locale)")
        
        self.outputDir = base
        self.elementsDir = base.appendingPathComponent("elements")
        
        try? FileManager.default.createDirectory(
            at: outputDir,
            withIntermediateDirectories: true
        )
        try? FileManager.default.createDirectory(
            at: elementsDir,
            withIntermediateDirectories: true
        )
    }
    
    @MainActor
    func startCapture() async {
        isCapturing = true
        currentStep = 0
        
        for step in steps {
            step.navigate()
            try? await Task.sleep(for: step.settle)
            captureWindow(name: step.name)
            step.cleanup?()
            currentStep += 1
        }
        
        isCapturing = false
        print("✅ Capture complete: \(outputDir.path)")
        exit(0)
    }
    
    private func captureWindow(name: String) {
        guard let window = UIApplication.shared
            .connectedScenes
            .compactMap({ $0 as? UIWindowScene })
            .first?.windows.first else { return }
        
        let renderer = UIGraphicsImageRenderer(bounds: window.bounds)
        let image = renderer.image { ctx in
            window.drawHierarchy(in: window.bounds, afterScreenUpdates: true)
        }
        
        let url = outputDir.appendingPathComponent("\(name).png")
        try? image.pngData()?.write(to: url)
    }
}

struct MarketingElementHarness {
    @MainActor
    static func renderElement<Content: View>(
        name: String,
        width: CGFloat,
        cornerRadius: CGFloat,
        background: Color,
        @ViewBuilder content: () -> Content
    ) {
        let renderer = ImageRenderer(content: content()
            .frame(width: width)
            .background(background)
            .clipShape(RoundedRectangle(cornerRadius: cornerRadius))
        )
        renderer.scale = 3.0
        
        guard let uiImage = renderer.uiImage else { return }
        
        let base = FileManager.default
            .urls(for: .documentDirectory, in: .userDomainMask)[0]
            .deletingLastPathComponent()
            .deletingLastPathComponent()
        
        let locale = Locale.current.language.languageCode?.identifier ?? "en"
        let elementsDir = base
            .appendingPathComponent("marketing/\(locale)/elements")
        
        try? FileManager.default.createDirectory(
            at: elementsDir,
            withIntermediateDirectories: true
        )
        
        let url = elementsDir.appendingPathComponent("\(name).png")
        try? uiImage.pngData()?.write(to: url)
    }
}
#endif

4. Navigation Pattern Examples

TabView Navigation

@Observable
final class AppCoordinator {
    var selectedTab = 0
    
    func setTab(_ index: Int) {
        selectedTab = index
    }
}

// In MarketingCapture.swift:
func makeSteps(coordinator: AppCoordinator) -> [CaptureStep] {
    [
        CaptureStep(
            name: "01-home",
            navigate: { coordinator.setTab(0) },
            settle: .seconds(0.5),
            cleanup: nil
        ),
        CaptureStep(
            name: "02-shelf",
            navigate: { coordinator.setTab(1) },
            settle: .seconds(0.5),
            cleanup: nil
        )
    ]
}

NavigationStack with Router

@Observable
final class Router {
    var path: [Route] = []
    
    func push(_ route: Route) {
        path.append(route)
    }
    
    func popToRoot() {
        path.removeAll()
    }
}

// Steps:
CaptureStep(
    name: "03-detail",
    navigate: {
        router.popToRoot()
        router.push(.coffeeDetail(coffee))
    },
    settle: .seconds(0.8),
    cleanup: { router.popToRoot() }
)

NavigationSplitView

@Observable
final class SplitCoordinator {
    var sidebarSelection: SidebarItem?
    var detailSelection: DetailItem?
}

// Steps:
CaptureStep(
    name: "04-settings",
    navigate: {
        coordinator.sidebarSelection = .settings
        coordinator.detailSelection = nil
    },
    settle: .seconds(0.6),
    cleanup: nil
)

5. Demo Data Seeding

#if DEBUG
@MainActor
func seedMarketingData(modelContext: ModelContext) {
    // Clear existing
    try? modelContext.delete(model: Coffee.self)
    
    // Seed deterministic data
    let coffees = [
        Coffee(
            name: "Morning Blend",
            roaster: "Blue Bottle",
            origin: "Ethiopia",
            notes: ["Blueberry", "Chocolate", "Citrus"],
            roastDate: Date().addingTimeInterval(-7 * 86400)
        ),
        Coffee(
            name: "Dark Horse",
            roaster: "Stumptown",
            origin: "Colombia",
            notes: ["Caramel", "Nuts", "Brown Sugar"],
            roastDate: Date().addingTimeInterval(-3 * 86400)
        )
    ]
    
    coffees.forEach { modelContext.insert($0) }
    try? modelContext.save()
}
#endif

6. Integrating with ContentView

struct ContentView: View {
    @Environment(\.modelContext) private var modelContext
    @State private var coordinator = AppCoordinator()
    
    #if DEBUG
    @State private var captureCoordinator: MarketingCaptureCoordinator?
    #endif
    
    var body: some View {
        TabView(selection: $coordinator.selectedTab) {
            HomeView()
                .tag(0)
                .tabItem { Label("Home", systemImage: "house") }
            
            ShelfView()
                .tag(1)
                .tabItem { Label("Shelf", systemImage: "books.vertical") }
        }
        #if DEBUG
        .task {
            if ProcessInfo.processInfo.environment["MARKETING_CAPTURE"] == "1" {
                seedMarketingData(modelContext: modelContext)
                
                let locale = Locale.current.language.languageCode?.identifier ?? "en"
                captureCoordinator = MarketingCaptureCoordinator(
                    steps: makeMarketingSteps(coordinator: coordinator),
                    locale: locale
                )
                
                try? await Task.sleep(for: .seconds(1))
                await captureCoordinator?.startCapture()
            }
        }
        #endif
    }
}

7. Element Rendering Examples

Widget Rendering

#if DEBUG
@MainActor
func renderWidgets() {
    let entry = PulseWidgetEntry(
        date: Date(),
        coffee: seedCoffee,
        recentBrew: seedBrew
    )
    
    // Small widget
    MarketingElementHarness.renderElement(
        name: "widget-pulse-small",
        width: 158,
        cornerRadius: 16,
        background: .white
    ) {
        PulseWidgetSmallView(entry: entry)
            .padding(16) // WidgetKit padding manually added
    }
    
    // Medium widget
    MarketingElementHarness.renderElement(
        name: "widget-pulse-medium",
        width: 338,
        cornerRadius: 16,
        background: .white
    ) {
        PulseWidgetMediumView(entry: entry)
            .padding(16)
    }
}
#endif

Card Rendering

#if DEBUG
@MainActor
func renderCards(coffees: [Coffee]) {
    for (index, coffee) in coffees.enumerated() {
        MarketingElementHarness.renderElement(
            name: "card-\(index + 1)",
            width: 380,
            cornerRadius: 20,
            background: Color(.systemBackground)
        ) {
            CoffeeCard(coffee: coffee)
                .padding(.horizontal, 16)
                .padding(.vertical, 12)
        }
    }
}
#endif

Chart Rendering

#if DEBUG
@MainActor
func renderCharts() {
    MarketingElementHarness.renderElement(
        name: "chart-cupping",
        width: 380,
        cornerRadius: 16,
        background: Color(.systemBackground)
    ) {
        CuppingRadarChart(scores: sampleScores)
            .frame(height: 300)
            .padding(20)
    }
}
#endif

8. Build and Launch Script

#!/bin/bash
# scripts/capture-marketing.sh

set -e

APP_NAME="YourApp"
SCHEME="YourApp"
DEVICE="iPhone 17"
IOS_VERSION="18.2"

LOCALES=("en" "de" "es" "fr" "ja")

SIMULATOR_ID=$(xcrun simctl list devices available | \
    grep "$DEVICE ($IOS_VERSION)" | head -1 | \
    grep -o '\[.*\]' | tr -d '[]')

if [ -z "$SIMULATOR_ID" ]; then
    echo "❌ Simulator '$DEVICE ($IOS_VERSION)' not found"
    exit 1
fi

echo "📱 Using simulator: $SIMULATOR_ID"

# Boot simulator
xcrun simctl boot "$SIMULATOR_ID" 2>/dev/null || true
sleep 2

# Build once
echo "🔨 Building..."
xcodebuild \
    -scheme "$SCHEME" \
    -destination "id=$SIMULATOR_ID" \
    -configuration Debug \
    -derivedDataPath ./build \
    build

APP_PATH=$(find ./build -name "${APP_NAME}.app" | head -1)
BUNDLE_ID=$(defaults read "$APP_PATH/Info.plist" CFBundleIdentifier)

echo "📦 Bundle ID: $BUNDLE_ID"

# Install once
xcrun simctl install "$SIMULATOR_ID" "$APP_PATH"

# Per-locale capture
for LOCALE in "${LOCALES[@]}"; do
    echo ""
    echo "📸 Capturing $LOCALE..."
    
    xcrun simctl launch \
        --terminate-running-process \
        "$SIMULATOR_ID" \
        "$BUNDLE_ID" \
        -AppleLanguages "($LOCALE)" \
        -MARKETING_CAPTURE 1
    
    sleep 8
done

# Copy to project
CONTAINER=$(xcrun simctl get_app_container "$SIMULATOR_ID" "$BUNDLE_ID" data)
cp -R "$CONTAINER/Documents/../../tmp/marketing" ./marketing/

echo ""
echo "✅ All locales captured → ./marketing/"

Critical Gotchas (Baked Into Skill)

1. Live Activities Persist Across Launches

Problem: Next locale crashes on stale SwiftData references.

Solution: End all Live Activities in cleanup:

for activity in Activity<PulseAttributes>.activities {
    await activity.end(nil, dismissalPolicy: .immediate)
}

2. Re-Seeding Per Locale

Problem: CloudKit sync churn causes crashes.

Solution: Seed once at launch, not per step.

3. ViewModels Setup Before Seed

Problem: VMs hold stale empty snapshots.

Solution: Seed data → wait 1s → instantiate VMs → capture.

4. Setting Trigger Binding to nil

Problem: Doesn't dismiss fullScreenCover — captures wrong sheet.

Solution: Use dedicated dismiss closure:

.fullScreenCover(isPresented: $showTimer) {
    TimerView(onDismiss: { showTimer = false })
}

5. NavigationPath Can't Be Popped Externally

Problem: Pushing over existing path captures wrong stack.

Solution: Always popToRoot() before push() in navigate block.

6. membershipExceptions Is an INCLUSION List

Problem: Widget target membership goes backwards — widget renders fail.

Solution: Set membershipExceptions for DEBUG files to exclude widget target:

// In pbxproj or via Xcode:
// MarketingCapture.swift → Target Membership → Widget ❌

7. ImageRenderer + ProgressView

Problem: Renders as prohibited symbol without explicit style.

Solution: Force .circular style:

ProgressView()
    .progressViewStyle(.circular)

8. .containerBackground Outside WidgetKit

Problem: No-op — widget renders have no background.

Solution: Manually add background in render harness:

WidgetView(entry: entry)
    .background(Color.white)

9. iPhone 8 Plus Gone on iOS 18+

Problem: Legacy 6.5" simulator unavailable.

Solution: Use iPhone 17 Pro Max or latest available large device.

10. Locale Launch Argument Format

Problem: Locale ignored if parens missing.

Solution: Always use "($LOCALE)" format:

-AppleLanguages "(de)"  # ✅
-AppleLanguages "de"    # ❌

11. SwiftUI Animations in ImageRenderer

Problem: Captures frame 0, not animated state.

Solution: Render non-animated state or use explicit .animation(nil).

Common Workflows

Capturing Timer Mid-Countdown

CaptureStep(
    name: "05-timer-active",
    navigate: {
        router.popToRoot()
        coordinator.startTimer(seconds: 180)
        // Timer view subscribes to coordinator.activeTimer
    },
    settle: .seconds(1.5), // Let timer animate in
    cleanup: {
        coordinator.stopTimer()
        router.popToRoot()
    }
)

Capturing Presented Sheet

@State private var showSheet = false

// Step:
CaptureStep(
    name: "06-add-coffee",
    navigate: {
        showSheet = true
    },
    settle: .seconds(0.8),
    cleanup: {
        showSheet = false
    }
)

// View:
.sheet(isPresented: $showSheet) {
    AddCoffeeView()
}

Rendering All Widgets

#if DEBUG
@MainActor
func renderAllWidgets() {
    let entries = [
        ("pulse-small", PulseWidgetSmallView.self, 158),
        ("pulse-medium", PulseWidgetMediumView.self, 338),
        ("pulse-large", PulseWidgetLargeView.self, 338)
    ]
    
    for (name, viewType, width) in entries {
        MarketingElementHarness.renderElement(
            name: "widget-\(name)",
            width: width,
            cornerRadius: 16,
            background: .white
        ) {
            viewType.init(entry: sampleEntry)
                .padding(16)
        }
    }
}
#endif

Troubleshooting

Capture exits immediately

Check: MARKETING_CAPTURE=1 env var is set in launch args.

xcrun simctl launch ... -MARKETING_CAPTURE 1

Locale not applied

Check: Parens in -AppleLanguages:

-AppleLanguages "(de)"  # ✅ Correct

Widget renders blank

Check: Manual padding added (WidgetKit adds 16pt automatically):

WidgetView(entry: entry)
    .padding(16)  // Add this

Screenshot captures wrong screen

Check: Settle duration long enough for animations:

settle: .seconds(0.8)  // Increase if needed

SwiftData crash on second locale

Check: Live Activities ended in cleanup:

cleanup: {
    Task {
        for activity in Activity<Attrs>.activities {
            await activity.end(nil, dismissalPolicy: .immediate)
        }
    }
}

Element renders with wrong size

Check: Frame width explicitly set:

content()
    .frame(width: 380)  // Must be explicit

Configuration Reference

Capture Step Properties

PropertyTypePurpose
nameStringFilename without extension (e.g. "01-home")
navigate@MainActor () -> VoidPut app in correct state
settleDurationWait time for animations
cleanup(@MainActor () -> Void)?Tear down before next step

Script Variables

VariableExamplePurpose
APP_NAME"YourApp"App target name
SCHEME"YourApp"Xcode scheme
DEVICE"iPhone 17"Simulator model
IOS_VERSION"18.2"iOS runtime
LOCALES("en" "de" "es")Locale codes

Environment Variables

Set in launch args or script:

MARKETING_CAPTURE=1        # Enable capture mode

Post-Processing

Use app-store-screenshots to composite captured PNGs into Apple-style marketing pages with device mockups, headlines, and gradients.

npx skills add ParthJadhav/app-store-screenshots

Then prompt:

Composite my marketing screenshots into App Store pages with device frames

When to Use This Skill

✅ Use when:

  • Capturing marketing screenshots for App Store
  • Rendering isolated components (cards, widgets, charts)
  • Multi-locale asset generation
  • SwiftUI-based iOS app with defined navigation
  • Need full control over demo data and app state

❌ Don't use when:

  • UIKit-based app (requires different capture approach)
  • XCUITest infrastructure already working well
  • Single locale, manual capture sufficient
  • App uses web views or external content (can't seed)

Example: Complete Coffee App Capture

#if DEBUG
@MainActor
func makeMarketingSteps(
    coordinator: AppCoordinator,
    router: Router,
    viewModel: HomeViewModel
) -> [CaptureStep] {
    let coffees = viewModel.coffees
    
    return [
        // Tab 1: Home
        CaptureStep(
            name: "01-home",
            navigate: { coordinator.setTab(0) },
            settle: .seconds(0.5),
            cleanup: nil
        ),
        
        // Tab 2: Shelf
        CaptureStep(
            name: "02-shelf",
            navigate: { coordinator.setTab(1) },
            settle: .seconds(0.5),
            cleanup: nil
        ),
        
        // Detail: First coffee
        CaptureStep(
            name: "03-detail",
            navigate: {
                coordinator.setTab(0)
                router.popToRoot()
                router.push(.coffeeDetail(coffees[0]))
            },
            settle: .seconds(0.8),
            cleanup: { router.popToRoot() }
        ),
        
        // Timer: Active brew
        CaptureStep(
            name: "04-timer",
            navigate: {
                coordinator.setTab(0)
                router.popToRoot()
                coordinator.startTimer(seconds: 180)
            },
            settle: .seconds(1.2),
            cleanup: {
                coordinator.stopTimer()
                router.popToRoot()
            }
        ),
        
        // Settings
        CaptureStep(
            name: "05-settings",
            navigate: { coordinator.setTab(2) },
            settle: .seconds(0.5),
            cleanup: nil
        ),
        
        // Elements: Cards
        CaptureStep(
            name: "elements",
            navigate: {
                for (index, coffee) in coffees.enumerated() {
                    MarketingElementHarness.renderElement(
                        name: "card-\(index + 1)",
                        width: 380,
                        cornerRadius: 20,
                        background: Color(.systemBackground)
                    ) {
                        CoffeeCard(coffee: coffee)
                            .padding(.horizontal, 16)
                            .padding(.vertical, 12)
                    }
                }
                
                renderAllWidgets()
            },
            settle: .seconds(0.1),
            cleanup: nil
        )
    ]
}
#endif

Run:

chmod +x scripts/capture-marketing.sh
./scripts/capture-marketing.sh

Output:

marketing/
    en/
        01-home.png
        02-shelf.png
        03-detail.png
        04-timer.png
        05-settings.png
        elements/
            card-1.png
            card-2.png
            widget-pulse-small.png
            widget-pulse-medium.png
    de/
        [same structure]
    es/
        [same structure]
    fr/
        [same structure]
    ja/
        [same structure]

Summary

This skill automates iOS marketing screenshot capture by:

  1. Gathering requirements — screens, elements, locales, device, appearance
  2. Generating capture system — DEBUG-gated coordinator + steps
  3. Seeding demo data — deterministic, locale-agnostic
  4. Navigating programmatically — TabView, NavigationStack, or SplitView
  5. Capturing window — drawHierarchy for full screenshots
  6. Rendering elements — ImageRenderer for isolated components
  7. Looping locales — simctl launch with -AppleLanguages

The result: one build, N launches, complete multi-locale marketing assets ready for App Store or further compositing.

FAQ

What does the ios-marketing-capture-automation skill do?

Automate multi-locale marketing screenshot capture for SwiftUI iOS apps with in-app capture system, demo data seeding, and element rendering.

How do I install ios-marketing-capture-automation?

Run npx skills add https://github.com/reason-machines/marketing-skills --skill ios-marketing-capture-automation in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/ios-marketing-capture-automation/ (personal) or .claude/skills/ios-marketing-capture-automation/ (project).

Which AI agents can use ios-marketing-capture-automation?

Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.

Is ios-marketing-capture-automation safe to install?

No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.

Who maintains ios-marketing-capture-automation?

It is published by reason-machines in reason-machines/marketing-skills. It has 1,029 recorded installs.

More from reason-machines/marketing-skills

View all →

marketing-for-founders-saas-growth

reason-machines/marketing-skills

Access curated marketing strategies and channels to acquire the first 10/100/1000 users for SaaS products and startups

Marketing & SEO1.1K installs

agentkits-marketing-automation

reason-machines/marketing-skills

Enterprise AI marketing automation toolkit with 18 agents, 93 commands, and 28 skills for campaign planning, content creation, SEO, CRO, and growth workflows

Marketing & SEO1K installs

SEO & content marketing skill suite with keyword research, content audits, technical SEO, competitor analysis, and AI-powered content workflows

Marketing & SEO1K installs

ai-marketing-skills-automation

reason-machines/marketing-skills

Open-source AI marketing automation skills for growth experiments, sales pipeline, content ops, outbound, SEO, and finance automation

Marketing & SEO1K installs

marketingskills-ai-agent-collection

reason-machines/marketing-skills

Marketing skills collection for AI agents - CRO, copywriting, SEO, analytics, and growth engineering

AI & Agents1K installs

ai-marketing-claude-suite

reason-machines/marketing-skills

Comprehensive AI marketing automation suite for Claude Code with 15 skills including website audits, copywriting, email sequences, ad campaigns, competitive analysis, and PDF reporting

Marketing & SEO1K installs

Popular mobile skills

View category →

design-mobile-apps

designed-by-ai/skills

Design mobile app screens with Sleek, edit Sleek projects, and implement their designs in React Native or HTML.

Mobile1.1M installs

ios-design

uizze.sh

Design and review iOS UI/UX for iPhone apps in SwiftUI, UIKit, or React Native. Use for mobile app design, screen redesigns, navigation flows, typography, forms, keyboard and safe-area layout, and accessibility checks. Apply Apple Human Interface Guidelines and consult real product references through the optional Uizze connection.

Mobile416.5K installs

vercel-react-native-skills

vercel-labs/agent-skills

React Native and Expo best practices for building performant mobile apps. Use when building React Native components, optimizing list performance, implementing animations, or working with native modules. Triggers on tasks involving React Native, Expo, mobile performance, or native platform APIs.

OfficialMobile256.7K installs

animate-expo

emilkowalski/skills

Build animations in React Native and Expo, making the decisions in the order that determines whether they feel right — should it animate, which thread it runs on, which properties, spring or timing, how the gesture hands off, how it degrades. Writes the implementation with Reanimated, Gesture Handler, Expo Router and expo-haptics. Use when animating anything in an Expo app, adding gestures, sheets, screen transitions, press feedback or haptics, or fixing motion that stutters on device. For web animation use animate.

Mobile140.1K installs

xcode-project-setup

firebase/agent-skills

Safely modifies Xcode projects (.pbxproj) to add Swift Packages and link files. Use this skill whenever an iOS project needs dependencies installed (e.g. Firebase, Alamofire).

OfficialMobile131.2K installs

firebase-remote-config-basics

firebase/agent-skills

Manages Firebase Remote Config templates, feature flags, loading strategies, and SDKs (Android, iOS). Use when downloading/deploying remoteconfig JSON templates, managing version history/feature flags, setting in-app defaults, fetchAndActivate(), real-time listeners, or SDK setup. Don't use for Firebase Hosting, Auth, Firestore, Data Connect, Crashlytics, or App Hosting.

OfficialMobile121.7K installs