Architecture patterns for SwiftUI applications including MVVM, navigation, dependency injection, and the @Observable macro.
Generates SwiftUI architecture patterns including MVVM, navigation, dependency injection, and the @Observable macro.
npx claudepluginhub arustydev/aiThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Architecture patterns for SwiftUI applications including MVVM, navigation, dependency injection, and the @Observable macro.
import SwiftUI
// ViewModel using @Observable macro (iOS 17+/macOS 14+)
@Observable
final class ContentViewModel {
var items: [Item] = []
var isLoading = false
var errorMessage: String?
private let repository: ItemRepository
init(repository: ItemRepository = .live) {
self.repository = repository
}
func loadItems() async {
isLoading = true
defer { isLoading = false }
do {
items = try await repository.fetchAll()
} catch {
errorMessage = error.localizedDescription
}
}
}
// View
struct ContentView: View {
@State private var viewModel = ContentViewModel()
var body: some View {
List(viewModel.items) { item in
ItemRow(item: item)
}
.overlay {
if viewModel.isLoading {
ProgressView()
}
}
.task {
await viewModel.loadItems()
}
}
}
// Protocol-based dependency
protocol ItemRepository: Sendable {
func fetchAll() async throws -> [Item]
func save(_ item: Item) async throws
}
// Live implementation
struct LiveItemRepository: ItemRepository {
func fetchAll() async throws -> [Item] {
// Real implementation
}
func save(_ item: Item) async throws {
// Real implementation
}
}
// Mock for testing
struct MockItemRepository: ItemRepository {
var items: [Item] = []
var shouldFail = false
func fetchAll() async throws -> [Item] {
if shouldFail { throw TestError.fetchFailed }
return items
}
func save(_ item: Item) async throws {
// Mock save
}
}
// Static factory pattern
extension ItemRepository where Self == LiveItemRepository {
static var live: LiveItemRepository { LiveItemRepository() }
}
extension ItemRepository where Self == MockItemRepository {
static var mock: MockItemRepository { MockItemRepository() }
}
// Type-safe navigation paths
enum NavigationDestination: Hashable {
case detail(Item)
case settings
case profile(userId: String)
}
struct RootView: View {
@State private var path = NavigationPath()
var body: some View {
NavigationStack(path: $path) {
ContentView(path: $path)
.navigationDestination(for: NavigationDestination.self) { destination in
switch destination {
case .detail(let item):
ItemDetailView(item: item)
case .settings:
SettingsView()
case .profile(let userId):
ProfileView(userId: userId)
}
}
}
}
}
// Environment key for repository
private struct ItemRepositoryKey: EnvironmentKey {
static let defaultValue: any ItemRepository = LiveItemRepository()
}
extension EnvironmentValues {
var itemRepository: any ItemRepository {
get { self[ItemRepositoryKey.self] }
set { self[ItemRepositoryKey.self] = newValue }
}
}
// Usage in view
struct ItemListView: View {
@Environment(\.itemRepository) private var repository
@State private var items: [Item] = []
var body: some View {
List(items) { item in
Text(item.name)
}
.task {
items = try? await repository.fetchAll() ?? []
}
}
}
// Preview with mock
#Preview {
ItemListView()
.environment(\.itemRepository, MockItemRepository(items: .preview))
}
MyApp/
├── App/
│ ├── MyApp.swift # @main entry point
│ ├── AppDelegate.swift # UIKit lifecycle (if needed)
│ └── AppState.swift # Global @Observable state
│
├── Features/ # Feature modules
│ ├── Home/
│ │ ├── Views/
│ │ │ ├── HomeView.swift
│ │ │ └── HomeCard.swift
│ │ ├── ViewModels/
│ │ │ └── HomeViewModel.swift
│ │ └── Models/
│ │ └── HomeItem.swift
│ │
│ ├── Profile/
│ │ ├── Views/
│ │ ├── ViewModels/
│ │ └── Models/
│ │
│ └── Settings/
│ └── ...
│
├── Core/ # Shared infrastructure
│ ├── Repositories/
│ │ └── ItemRepository.swift
│ ├── Services/
│ │ ├── Network/
│ │ │ ├── APIClient.swift
│ │ │ ├── Endpoints.swift
│ │ │ └── NetworkError.swift
│ │ └── Persistence/
│ │ ├── DatabaseManager.swift
│ │ └── KeychainService.swift
│ └── Extensions/
│ ├── View+Extensions.swift
│ └── Date+Extensions.swift
│
├── Shared/ # Reusable UI components
│ ├── Components/
│ │ ├── LoadingView.swift
│ │ ├── ErrorView.swift
│ │ └── AsyncButton.swift
│ ├── Modifiers/
│ │ ├── CardStyle.swift
│ │ └── ShimmerEffect.swift
│ └── Styles/
│ └── ButtonStyles.swift
│
├── Utilities/ # Helpers and constants
│ ├── Constants.swift
│ ├── Logger.swift
│ └── Formatters.swift
│
├── Resources/
│ ├── Assets.xcassets # Images and colors
│ ├── Localizable.xcstrings # Localized strings (Xcode 15+)
│ └── Fonts/ # Custom fonts
│ └── CustomFont.ttf
│
└── Tests/
├── UnitTests/
│ ├── ViewModels/
│ │ └── HomeViewModelTests.swift
│ └── Services/
│ └── APIClientTests.swift
└── UITests/
├── HomeUITests.swift
└── Helpers/
└── XCUIApplication+Extensions.swift
MyAppPackage/
├── Package.swift
├── Sources/
│ ├── MyApp/ # Main app target
│ │ └── MyApp.swift
│ ├── Features/ # Feature library
│ │ ├── Home/
│ │ └── Profile/
│ ├── Core/ # Core library
│ │ ├── Networking/
│ │ └── Persistence/
│ └── SharedUI/ # UI components library
│ ├── Components/
│ └── Modifiers/
└── Tests/
├── CoreTests/
└── FeaturesTests/
Activates when the user asks about AI prompts, needs prompt templates, wants to search for prompts, or mentions prompts.chat. Use for discovering, retrieving, and improving prompts.
Search, retrieve, and install Agent Skills from the prompts.chat registry using MCP tools. Use when the user asks to find skills, browse skill catalogs, install a skill for Claude, or extend Claude's capabilities with reusable AI agent components.
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.