jx1100370217

ios-app-architecture

1
0
# Install this skill:
npx skills add jx1100370217/my-openclaw-skills --skill "ios-app-architecture"

Install specific skill from multi-skill repository

# Description

Design scalable iOS app architectures. Use when setting up project structure, implementing MVVM/TCA/VIPER patterns, managing dependencies, organizing modules, or designing data flow. Covers clean architecture, composition, testing strategies, and modularization for maintainable iOS apps.

# SKILL.md


name: ios-app-architecture
description: Design scalable iOS app architectures. Use when setting up project structure, implementing MVVM/TCA/VIPER patterns, managing dependencies, organizing modules, or designing data flow. Covers clean architecture, composition, testing strategies, and modularization for maintainable iOS apps.


iOS App Architecture

Design and implement scalable, testable, and maintainable iOS applications.

Architecture Patterns

View ←→ ViewModel ←→ Model
         ↓
      Services
// Model
struct User: Identifiable, Codable {
    let id: UUID
    var name: String
    var email: String
}

// ViewModel
@MainActor
class UserViewModel: ObservableObject {
    @Published private(set) var users: [User] = []
    @Published private(set) var state: ViewState = .idle

    private let userService: UserServiceProtocol

    init(userService: UserServiceProtocol = UserService()) {
        self.userService = userService
    }

    func loadUsers() async {
        state = .loading
        do {
            users = try await userService.fetchUsers()
            state = .loaded
        } catch {
            state = .error(error)
        }
    }
}

// View
struct UserListView: View {
    @StateObject private var viewModel = UserViewModel()

    var body: some View {
        Group {
            switch viewModel.state {
            case .idle, .loading:
                ProgressView()
            case .loaded:
                List(viewModel.users) { user in
                    UserRow(user: user)
                }
            case .error(let error):
                ErrorView(error: error)
            }
        }
        .task { await viewModel.loadUsers() }
    }
}

The Composable Architecture (TCA)

Best for complex apps needing predictable state management.

import ComposableArchitecture

@Reducer
struct UserFeature {
    @ObservableState
    struct State: Equatable {
        var users: [User] = []
        var isLoading = false
    }

    enum Action {
        case loadUsers
        case usersResponse(Result<[User], Error>)
    }

    @Dependency(\.userClient) var userClient

    var body: some ReducerOf<Self> {
        Reduce { state, action in
            switch action {
            case .loadUsers:
                state.isLoading = true
                return .run { send in
                    await send(.usersResponse(
                        Result { try await userClient.fetchUsers() }
                    ))
                }
            case .usersResponse(.success(let users)):
                state.isLoading = false
                state.users = users
                return .none
            case .usersResponse(.failure):
                state.isLoading = false
                return .none
            }
        }
    }
}

Project Structure

MyApp/
β”œβ”€β”€ App/
β”‚   β”œβ”€β”€ MyAppApp.swift
β”‚   └── AppDelegate.swift
β”œβ”€β”€ Features/
β”‚   β”œβ”€β”€ Home/
β”‚   β”‚   β”œβ”€β”€ HomeView.swift
β”‚   β”‚   β”œβ”€β”€ HomeViewModel.swift
β”‚   β”‚   └── Components/
β”‚   β”œβ”€β”€ Profile/
β”‚   └── Settings/
β”œβ”€β”€ Core/
β”‚   β”œβ”€β”€ Models/
β”‚   β”œβ”€β”€ Services/
β”‚   β”‚   β”œβ”€β”€ Networking/
β”‚   β”‚   └── Persistence/
β”‚   └── Utilities/
β”œβ”€β”€ UI/
β”‚   β”œβ”€β”€ Components/
β”‚   β”œβ”€β”€ Styles/
β”‚   └── Extensions/
└── Resources/
    β”œβ”€β”€ Assets.xcassets
    └── Localizable.strings

Dependency Injection

Protocol-Based DI

// Protocol
protocol UserServiceProtocol {
    func fetchUsers() async throws -> [User]
}

// Live Implementation
class UserService: UserServiceProtocol {
    func fetchUsers() async throws -> [User] {
        // Real API call
    }
}

// Mock for Testing
class MockUserService: UserServiceProtocol {
    var usersToReturn: [User] = []

    func fetchUsers() async throws -> [User] {
        usersToReturn
    }
}

Environment-Based DI

// Dependency Container
class Dependencies: ObservableObject {
    let userService: UserServiceProtocol
    let analyticsService: AnalyticsProtocol

    init(
        userService: UserServiceProtocol = UserService(),
        analyticsService: AnalyticsProtocol = AnalyticsService()
    ) {
        self.userService = userService
        self.analyticsService = analyticsService
    }

    static let preview = Dependencies(
        userService: MockUserService(),
        analyticsService: MockAnalyticsService()
    )
}

// Usage
@main
struct MyApp: App {
    @StateObject private var dependencies = Dependencies()

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(dependencies)
        }
    }
}

Modularization

Swift Package Structure

Packages/
β”œβ”€β”€ Core/
β”‚   └── Package.swift
β”œβ”€β”€ Networking/
β”‚   └── Package.swift
β”œβ”€β”€ FeatureHome/
β”‚   └── Package.swift
└── DesignSystem/
    └── Package.swift
// Package.swift
let package = Package(
    name: "FeatureHome",
    platforms: [.iOS(.v17)],
    products: [
        .library(name: "FeatureHome", targets: ["FeatureHome"])
    ],
    dependencies: [
        .package(path: "../Core"),
        .package(path: "../DesignSystem")
    ],
    targets: [
        .target(
            name: "FeatureHome",
            dependencies: ["Core", "DesignSystem"]
        ),
        .testTarget(
            name: "FeatureHomeTests",
            dependencies: ["FeatureHome"]
        )
    ]
)

Testing Strategy

Unit Tests

@Test
func testUserViewModel() async {
    let mockService = MockUserService()
    mockService.usersToReturn = [User(id: UUID(), name: "Test", email: "[email protected]")]

    let viewModel = UserViewModel(userService: mockService)
    await viewModel.loadUsers()

    #expect(viewModel.users.count == 1)
    #expect(viewModel.state == .loaded)
}

Snapshot Tests

import SnapshotTesting

func testUserRowSnapshot() {
    let view = UserRow(user: .preview)
    assertSnapshot(of: view, as: .image(layout: .device(config: .iPhone13)))
}

Best Practices

  1. Single Responsibility - Each component does one thing well
  2. Dependency Inversion - Depend on abstractions, not implementations
  3. Composition over Inheritance - Prefer protocols and structs
  4. Unidirectional Data Flow - State flows down, actions flow up
  5. Testability First - Design for testing from the start

Resources

See references/architecture-patterns.md for detailed patterns.
See references/testing-guide.md for testing strategies.

# 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.