npx skills add dpearson2699/swift-ios-skills --skill swift-architecture
pluginagentmarketplace/custom-plugin-swift
swift-architecture
Master iOS/macOS app architecture - MVVM, Clean Architecture, Coordinator, DI, Repository
Installation
npx skills add pluginagentmarketplace/custom-plugin-swift --skill swift-architecture
Similar popular skills
Related neighbors and high-traction skills in the same topics — useful to compare before installing.
Helps users discover and install agent skills when they ask questions like "how do I do X", "fi…
3.3M installsBrowser automation CLI for AI agents. Use when the user needs to interact with websites, includ…
810.4K installsReview UI code for Web Interface Guidelines compliance. Use when asked to "review my UI", "chec…
617.3K installsBuild, deploy, evaluate, optimize, fine-tune, and manage Microsoft Foundry agents, models, and …
576.5K installsPrepare azd-based Azure projects for deployment: generates azure.yaml, infrastructure (Bicep/Te…
568.3K installsAlso in this package
Other skills from pluginagentmarketplace/custom-plugin-swift · top by installs.
npx skills add pluginagentmarketplace/custom-plugin-swift
More details
Agent compatibility
Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.
Also listed on
Alternate registries and mirrors of this skill.
npx skills add smithery/pluginagentmarketplace --skill swift-architecture
Repository health
main
Skill metadata
Parsed from SKILL.md frontmatter.
Package contents
Files included with this skill beyond the listing page.
-
skill md
SKILL.md11,507 B -
docs
SUMMARY.md115 B
History
- First seen on skills.sh
- First recorded snapshot · 21 installs
SKILL.md
Swift Architecture Skill
Design patterns and architectural approaches for scalable, testable Swift applications.
Prerequisites
- Understanding of SOLID principles
- Familiarity with dependency injection
- Experience with protocol-oriented programming
Parameters
parameters:
architecture_pattern:
type: string
enum: [mvvm, mvc, tca, viper, clean]
default: mvvm
navigation_pattern:
type: string
enum: [coordinator, router, navigation_stack]
default: coordinator
di_approach:
type: string
enum: [manual, container, property_wrapper]
default: manual
Topics Covered
Architecture Patterns
| Pattern | Complexity | Testability | Best For |
|---|---|---|---|
| MVC | Low | Low | Simple apps |
| MVVM | Medium | High | Most apps |
| Clean | High | Very High | Large teams |
| TCA | High | Very High | Complex state |
| VIPER | Very High | Very High | Enterprise |
Key Principles
| Principle | Description |
|---|---|
| Separation of Concerns | Each layer has one job |
| Dependency Inversion | Depend on abstractions |
| Single Source of Truth | One place for state |
| Unidirectional Data Flow | State → View → Action → State |
Layer Responsibilities
| Layer | Responsibility |
|---|---|
| View | UI rendering only |
| ViewModel | Presentation logic |
| UseCase | Business logic |
| Repository | Data access |
| Service | External integrations |
Code Examples
MVVM with Coordinator
// MARK: - Coordinator Protocol
protocol Coordinator: AnyObject {
var navigationController: UINavigationController { get }
var childCoordinators: [Coordinator] { get set }
func start()
}
extension Coordinator {
func addChild(_ coordinator: Coordinator) {
childCoordinators.append(coordinator)
}
func removeChild(_ coordinator: Coordinator) {
childCoordinators.removeAll { $0 === coordinator }
}
}
// MARK: - App Coordinator
final class AppCoordinator: Coordinator {
let navigationController: UINavigationController
var childCoordinators: [Coordinator] = []
private let dependencies: AppDependencies
init(navigationController: UINavigationController, dependencies: AppDependencies) {
self.navigationController = navigationController
self.dependencies = dependencies
}
func start() {
if dependencies.authService.isLoggedIn {
showMain()
} else {
showLogin()
}
}
private func showLogin() {
let coordinator = LoginCoordinator(
navigationController: navigationController,
dependencies: dependencies
)
coordinator.delegate = self
addChild(coordinator)
coordinator.start()
}
private func showMain() {
let coordinator = MainCoordinator(
navigationController: navigationController,
dependencies: dependencies
)
addChild(coordinator)
coordinator.start()
}
}
extension AppCoordinator: LoginCoordinatorDelegate {
func loginDidComplete(_ coordinator: LoginCoordinator) {
removeChild(coordinator)
showMain()
}
}
// MARK: - ViewModel
@MainActor
protocol ProductListViewModelProtocol: ObservableObject {
var products: [Product] { get }
var isLoading: Bool { get }
var error: Error? { get }
func loadProducts() async
func selectProduct(_ product: Product)
}
@MainActor
final class ProductListViewModel: ProductListViewModelProtocol {
@Published private(set) var products: [Product] = []
@Published private(set) var isLoading = false
@Published private(set) var error: Error?
private let getProductsUseCase: GetProductsUseCaseProtocol
private weak var coordinator: ProductCoordinator?
init(getProductsUseCase: GetProductsUseCaseProtocol, coordinator: ProductCoordinator) {
self.getProductsUseCase = getProductsUseCase
self.coordinator = coordinator
}
func loadProducts() async {
isLoading = true
error = nil
do {
products = try await getProductsUseCase.execute()
} catch {
self.error = error
}
isLoading = false
}
func selectProduct(_ product: Product) {
coordinator?.showProductDetail(product)
}
}
Clean Architecture Layers
// MARK: - Domain Layer (Use Cases)
protocol GetProductsUseCaseProtocol {
func execute() async throws -> [Product]
}
final class GetProductsUseCase: GetProductsUseCaseProtocol {
private let repository: ProductRepositoryProtocol
init(repository: ProductRepositoryProtocol) {
self.repository = repository
}
func execute() async throws -> [Product] {
let products = try await repository.getProducts()
// Business logic: filter, sort, validate
return products.filter { $0.isAvailable }.sorted { $0.name < $1.name }
}
}
// MARK: - Data Layer (Repository)
protocol ProductRepositoryProtocol {
func getProducts() async throws -> [Product]
func getProduct(id: String) async throws -> Product
func saveProduct(_ product: Product) async throws
}
final class ProductRepository: ProductRepositoryProtocol {
private let remoteDataSource: ProductRemoteDataSourceProtocol
private let localDataSource: ProductLocalDataSourceProtocol
init(remoteDataSource: ProductRemoteDataSourceProtocol,
localDataSource: ProductLocalDataSourceProtocol) {
self.remoteDataSource = remoteDataSource
self.localDataSource = localDataSource
}
func getProducts() async throws -> [Product] {
// Try cache first
if let cached = try? await localDataSource.getProducts(), !cached.isEmpty {
// Refresh in background
Task {
if let remote = try? await remoteDataSource.fetchProducts() {
try? await localDataSource.saveProducts(remote)
}
}
return cached
}
// Fetch from remote
let products = try await remoteDataSource.fetchProducts()
try? await localDataSource.saveProducts(products)
return products
}
func getProduct(id: String) async throws -> Product {
try await remoteDataSource.fetchProduct(id: id)
}
func saveProduct(_ product: Product) async throws {
try await remoteDataSource.createProduct(product)
try await localDataSource.saveProduct(product)
}
}
Dependency Injection Container
// MARK: - Dependencies Protocol
protocol HasAuthService {
var authService: AuthServiceProtocol { get }
}
protocol HasProductRepository {
var productRepository: ProductRepositoryProtocol { get }
}
typealias AppDependencies = HasAuthService & HasProductRepository
// MARK: - DI Container
final class DependencyContainer: AppDependencies {
// Singletons
lazy var authService: AuthServiceProtocol = AuthService()
// Factories
lazy var productRepository: ProductRepositoryProtocol = {
ProductRepository(
remoteDataSource: ProductRemoteDataSource(apiClient: apiClient),
localDataSource: ProductLocalDataSource(database: database)
)
}()
private lazy var apiClient: APIClientProtocol = APIClient()
private lazy var database: DatabaseProtocol = Database()
// Factory methods for ViewModels
func makeProductListViewModel(coordinator: ProductCoordinator) -> ProductListViewModel {
ProductListViewModel(
getProductsUseCase: GetProductsUseCase(repository: productRepository),
coordinator: coordinator
)
}
}
// MARK: - Property Wrapper Approach
@propertyWrapper
struct Injected<T> {
private let keyPath: KeyPath<DependencyContainer, T>
var wrappedValue: T {
DependencyContainer.shared[keyPath: keyPath]
}
init(_ keyPath: KeyPath<DependencyContainer, T>) {
self.keyPath = keyPath
}
}
// Usage
final class SomeService {
@Injected(\.authService) private var authService
}
SwiftUI MVVM
// MARK: - View
struct ProductListView: View {
@StateObject private var viewModel: ProductListViewModel
init(viewModel: @autoclosure @escaping () -> ProductListViewModel) {
_viewModel = StateObject(wrappedValue: viewModel())
}
var body: some View {
Group {
if viewModel.isLoading {
ProgressView()
} else if let error = viewModel.error {
ErrorView(error: error) {
Task { await viewModel.loadProducts() }
}
} else {
productList
}
}
.navigationTitle("Products")
.task {
await viewModel.loadProducts()
}
}
private var productList: some View {
List(viewModel.products) { product in
ProductRow(product: product)
.onTapGesture {
viewModel.selectProduct(product)
}
}
}
}
// MARK: - SwiftUI Coordinator (Router)
@MainActor
final class Router: ObservableObject {
@Published var path = NavigationPath()
func push<T: Hashable>(_ value: T) {
path.append(value)
}
func pop() {
path.removeLast()
}
func popToRoot() {
path.removeLast(path.count)
}
}
struct ContentView: View {
@StateObject private var router = Router()
@StateObject private var dependencies = DependencyContainer()
var body: some View {
NavigationStack(path: $router.path) {
ProductListView(viewModel: dependencies.makeProductListViewModel(router: router))
.navigationDestination(for: Product.self) { product in
ProductDetailView(product: product)
}
}
.environmentObject(router)
}
}
Troubleshooting
Common Issues
| Issue | Cause | Solution |
|---|---|---|
| Massive ViewModel | Too many responsibilities | Split into smaller VMs or use UseCases |
| Tight coupling | Direct dependencies | Use protocols and DI |
| Hard to test | Static/singleton dependencies | Inject dependencies |
| Memory leaks | Strong coordinator references | Use weak delegates |
| State sync issues | Multiple sources of truth | Single source + binding |
Debug Tips
// Check retain cycles
deinit {
print("\(Self.self) deinit")
}
// Trace view updates
var body: some View {
let _ = Self._printChanges()
// ...
}
// Validate architecture
// Run: swift package diagnose-api-breaking-changes
Validation Rules
validation:
- rule: layer_separation
severity: error
check: Views should not import data layer
- rule: protocol_abstractions
severity: warning
check: Dependencies should be protocols
- rule: unidirectional_flow
severity: info
check: State changes flow in one direction
Usage
Skill("swift-architecture")
Related Skills
swift-fundamentals- Protocol-oriented designswift-swiftui- SwiftUI patternsswift-testing- Testing architecture