ios-app-architecture
1
总安装量
1
周安装量
#76367
全站排名
安装命令
npx skills add https://github.com/jx1100370217/my-openclaw-skills --skill ios-app-architecture
Agent 安装分布
amp
1
cline
1
opencode
1
cursor
1
kimi-cli
1
codex
1
Skill 文档
iOS App Architecture
Design and implement scalable, testable, and maintainable iOS applications.
Architecture Patterns
MVVM (Recommended for SwiftUI)
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: "test@test.com")]
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
- Single Responsibility – Each component does one thing well
- Dependency Inversion – Depend on abstractions, not implementations
- Composition over Inheritance – Prefer protocols and structs
- Unidirectional Data Flow – State flows down, actions flow up
- 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.