Use when creating iOS apps, setting up Xcode projects, designing app architecture, implementing SwiftUI views, using SwiftData models, adding Swift 6 concurrency with actors, or integrating Supabase backend. Triggers on "new iOS app", "Swift architecture", "SwiftData setup", "actor pattern", "iOS project structure".
Provides guidance for building iOS apps with SwiftUI, SwiftData, Swift 6 actors, and Supabase backend integration. Triggers on "new iOS app", "Swift architecture", "SwiftData setup", "actor pattern", or "iOS project structure".
/plugin marketplace add bgrober/indie-stack/plugin install superpowers@superpowers-devThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Guide for building iOS apps with SwiftUI, SwiftData, Swift 6 concurrency, and Supabase backend.
AppName/
├── AppNameApp.swift # App entry, configures appearance and SwiftData
├── Models/
│ ├── Item.swift # SwiftData models (local persistence)
│ ├── ItemResult.swift # Enum: pending/success/failed states
│ └── Enums/ # Supporting enums (ItemType, etc.)
├── Services/
│ ├── AuthService.swift # @Observable auth state, trial flow
│ ├── SupabaseClient.swift # Actor for auth/token management
│ ├── SupabaseService.swift # Actor: storage upload and DB sync
│ ├── SyncService.swift # Actor: bidirectional sync
│ ├── ItemService.swift # Actor: business logic for items
│ ├── ImageManager.swift # Local image caching
│ ├── NetworkMonitor.swift # NWPathMonitor for connectivity
│ └── PermissionsManager.swift # Camera, photos, etc.
├── Utilities/
│ ├── Constants.swift # Supabase URL, anon key
│ └── Theme.swift # Design system, colors, typography
├── ViewModels/
│ └── FeatureViewModel.swift # Complex feature state machines
└── Views/
├── RootView.swift # Routes based on auth state
├── MainTabView.swift # Tab navigation
├── SplashView.swift # Launch animation
├── Auth/ # SignInView, TrialPrompt
├── Feature/ # Feature-specific views
├── Components/ # Reusable: badges, cards, empty states
├── Onboarding/ # OnboardingFlow
└── Settings/ # SettingsView
Services as Actors:
actor SupabaseService {
private let client: SupabaseClient
func uploadImage(_ data: Data, path: String) async throws -> URL {
// Actor-isolated state is thread-safe
}
}
UI-Bound State with @MainActor:
@MainActor @Observable
final class AuthService {
var isAuthenticated = false
var currentUser: User?
func signIn() async throws {
// All state mutations happen on MainActor
}
}
Sendable Data Transfer:
struct ItemSyncData: Sendable {
let id: UUID
let title: String
let createdAt: Date
}
Bridging SwiftData:
@preconcurrency import SwiftData
@Model
final class Item: @unchecked Sendable {
// All access via MainActor
}
Model with Denormalized Fields:
@Model
final class Item {
var id: UUID
var createdAt: Date
var title: String
// Denormalized for efficient queries
var resultType: String // "pending", "success", "failed"
var score: Int?
// Full result stored as associated value
var result: ItemResult = .pending
init(title: String) {
self.id = UUID()
self.createdAt = Date()
self.title = title
self.resultType = "pending"
}
}
Result Enum Pattern:
enum ItemResult: Codable, Equatable {
case pending
case success(SuccessData)
case failed(String)
}
AuthService Structure:
@MainActor @Observable
final class AuthService {
private let supabaseClient: SupabaseClient
var isAuthenticated = false
var isTrialUsed = false
var currentUser: User?
func checkAuthState() async {
// Check Keychain for existing session
}
func signInWithApple() async throws {
// Apple Sign-In → Supabase Auth
}
func signOut() async {
// Clear Keychain, reset state
}
}
Root View Routing:
struct RootView: View {
@Environment(AuthService.self) var authService
var body: some View {
Group {
if authService.isAuthenticated {
MainTabView()
} else {
SignInView()
}
}
}
}
Upload on Create:
// In ViewModel after creating local item
Task {
await supabaseService.syncItem(item)
}
Download on Login:
// In AuthService after successful login
await syncService.syncRemoteItems()
Conflict Resolution:
// Use upsert to handle conflicts
func syncItem(_ item: Item) async throws {
try await client
.from("items")
.upsert(item.toRemoteData(), onConflict: "id")
.execute()
}
Automatic 401 Retry:
func makeAuthenticatedRequest<T>(_ request: () async throws -> T) async throws -> T {
do {
return try await request()
} catch let error as HTTPError where error.statusCode == 401 {
try await refreshToken()
return try await request() // Retry once
}
}
Theme.swift Structure:
// Colors
extension Color {
static let appAccent = Color("AccentColor")
static let cardBackground = Color("CardBackground")
}
// Typography
extension View {
func appHeadline() -> some View {
self.font(.system(.headline, design: .rounded))
}
}
// View Modifiers
extension View {
func appCard() -> some View {
self.padding()
.background(Color.cardBackground)
.cornerRadius(12)
}
}
// Haptics
struct HapticManager {
static func impact(_ style: UIImpactFeedbackGenerator.FeedbackStyle) {
let generator = UIImpactFeedbackGenerator(style: style)
generator.impactOccurred()
}
}
| Type | Convention | Example |
|---|---|---|
| SwiftData Model | Singular noun | Pour.swift |
| Service Actor | *Service.swift | GradingService.swift |
| View | *View.swift | CaptureView.swift |
| ViewModel | *ViewModel.swift | CaptureViewModel.swift |
| Result Enum | *Result.swift | PourResult.swift |
| Manager | *Manager.swift | ImageManager.swift |
ModelContext across actor boundaries@preconcurrency import SwiftData@unchecked Sendable if accessed only via MainActornonisolated for pure functions that don't access actor stateSWIFT_APPROACHABLE_CONCURRENCY = YES build settingSendable structs for data transfer between actors[ ] Create Xcode project with SwiftData
[ ] Set up folder structure (Models, Services, Views, etc.)
[ ] Add Supabase Swift SDK
[ ] Create Constants.swift with Supabase URL/key
[ ] Create Theme.swift with design system
[ ] Set up AuthService with Apple Sign-In
[ ] Create SupabaseClient actor
[ ] Add NetworkMonitor for connectivity
[ ] Create RootView with auth routing
[ ] Set up MainTabView navigation
[ ] Enable Swift 6 strict concurrency mode
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.