From cc-mobile-ios
How MVVM + Clean Architecture is applied in this Swift + SwiftUI codebase. Load when designing a new feature, deciding where code belongs, adding a repository or use case, or reviewing layer boundaries.
How this skill is triggered — by the user, by Claude, or both
Slash command
/cc-mobile-ios:clean-architecture-iosThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
```
Presentation — SwiftUI views + @Observable ViewModels + ViewState. Knows SwiftUI.
↓
Domain — Pure Swift. Structs, enums, protocols, use cases. No SwiftUI / UIKit / URLSession / SwiftData.
↑
Data — Repository implementations. Knows URLSession, SwiftData, Keychain, etc.
Dependency rule: Presentation → Domain ← Data. Arrows never reverse. Both outer layers depend on Domain; Domain depends on nothing outside Foundation + Swift.
What lives here:
Sendable, Equatable, usually Hashable. No framework annotations.// Domain/Model/Order.swift
struct Order: Equatable, Sendable {
let id: OrderID
let items: [OrderItem]
let total: Money
}
// Domain/Repository/OrderRepository.swift
protocol OrderRepository: Sendable {
func get(id: OrderID) async throws -> Order
func observe() -> AsyncStream<[Order]>
}
// Domain/UseCase/SubmitOrderUseCase.swift
struct SubmitOrderUseCase {
private let orders: OrderRepository
private let now: () -> Date
init(orders: OrderRepository, now: @escaping () -> Date = Date.init) {
self.orders = orders
self.now = now
}
func callAsFunction(_ draft: OrderDraft) async throws -> Order { /* ... */ }
}
What lives here:
Codable).// Data/Remote/OrderDTO.swift
struct OrderDTO: Decodable {
let id: String
let items: [OrderItemDTO]
let totalCents: Int
let createdAt: String
}
// Data/Mapper/OrderMapper.swift
extension OrderDTO {
func toDomain() -> Order {
Order(
id: OrderID(id),
items: items.map { $0.toDomain() },
total: .cents(totalCents)
)
}
}
// Data/Repository/LiveOrderRepository.swift
final class LiveOrderRepository: OrderRepository {
private let client: APIClient
init(client: APIClient) { self.client = client }
func get(id: OrderID) async throws -> Order {
do {
let dto: OrderDTO = try await client.get("/orders/\(id.raw)")
return dto.toDomain()
} catch let error as URLError {
throw DomainError.network(code: error.code.rawValue)
} catch is DecodingError {
throw DomainError.invalidResponse
}
}
}
What lives here:
swiftui-views skill.@Observable @MainActor final class, exposing ViewState, calling use cases.Add one when:
Skip it when:
repository.foo() and return. Inject the repository directly in that case — but only if the rest of the codebase is consistent about this.Feature_X/Data, Feature_X/Domain, Feature_X/Presentation.Core, Networking, Persistence, Feature_X.When adding a feature, every item below should exist:
Domain/Model/Domain/Repository/Domain/UseCase/ (if warranted)Data/Remote/ and Data/Mapper/Data/Remote/Live…Repository (or named) in Data/Repository/Presentation/<Feature>/@Observable ViewModel with constructor-injected dependencies#PreviewAppRoutenpx claudepluginhub dimitriremoiville/cc-mobile --plugin cc-mobile-iosSearches MemPalace before answering questions about past work, people, projects, or prior decisions. Returns verbatim stored content instead of guessing from model memory.
Guides Payload CMS config (payload.config.ts), collections, fields, hooks, access control, APIs. Debugs validation errors, security, relationships, queries, transactions, hook behavior.
Implements vector databases with Pinecone, Weaviate, Qdrant, Milvus, pgvector for semantic search, RAG, recommendations, and similarity systems. Optimizes embeddings, indexing, and hybrid search.