0Pricing
Swift Academy · Lesson

Layers: Domain, Data and Presentation

Separating business logic from data access and UI in a Swift app.

Layers: Domain, Data and Presentation is a free Swift Academy lesson on CoddyKit — lesson 1 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Swift Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

Clean Architecture in Swift

Clean Architecture separates code into layers: Domain (business logic), Data (persistence/network), and Presentation (UI).

// Domain: pure Swift, no framework imports
// Data: Core Data / URLSession / JSON
// Presentation: SwiftUI / UIKit

The Domain Layer

The Domain layer contains business entities, use cases, and repository protocols. It has no dependencies on UIKit, SwiftUI, or databases.

// Domain/Entities/User.swift
struct User {
  let id: Int
  var name: String
  var email: String
}
// No import UIKit, no import SwiftData

Use Cases in the Domain

A use case encapsulates one specific business operation. It depends only on repository protocols from the domain.

// Domain/UseCases/FetchUserUseCase.swift
struct FetchUserUseCase {
  let repo: UserRepository
  func execute(id: Int) async throws -> User {
    try await repo.getUser(id: id)
  }
}

Repository Protocols in the Domain

Repository protocols define the data access contract. The domain owns the protocol; the data layer provides the implementation.

// Domain/Repositories/UserRepository.swift
protocol UserRepository {
  func getUser(id: Int) async throws -> User
  func saveUser(_ user: User) async throws
}

The Data Layer

The Data layer implements domain protocols using concrete technologies (URLSession, Core Data, SwiftData, Keychain).

// Data/Repositories/RemoteUserRepository.swift
struct RemoteUserRepository: UserRepository {
  func getUser(id: Int) async throws -> User {
    let data = try await URLSession.shared.data(from: userURL(id)).0
    return try JSONDecoder().decode(User.self, from: data)
  }
  func saveUser(_ user: User) async throws { /* PUT request */ }
}

Data Transfer Objects

DTOs are Codable types used in the data layer to decode API responses, then mapped to domain entities.

struct UserDTO: Decodable {
  var user_id: Int
  var full_name: String
  func toDomain() -> User { User(id: user_id, name: full_name, email: "") }
}

The Presentation Layer

The Presentation layer contains ViewModels and SwiftUI views. It calls use cases and exposes observable state.

// Presentation/ViewModels/ProfileViewModel.swift
@MainActor
final class ProfileViewModel: ObservableObject {
  @Published var user: User?
  private let useCase: FetchUserUseCase
  init(useCase: FetchUserUseCase) { self.useCase = useCase }
  func load(id: Int) async {
    user = try? await useCase.execute(id: id)
  }
}

Dependency Direction

Dependencies point inward: Presentation depends on Domain; Data depends on Domain. Domain depends on nothing.

// Outer → Inner dependency rule:
Presentation → Domain ← Data
// Domain is the stable core; outer layers are swappable

Folder Structure

Organise Swift files by layer then by feature for large apps.

// MyApp/
// ├── Domain/
// │   ├── Entities/
// │   ├── UseCases/
// │   └── Repositories/  (protocols)
// ├── Data/
// │   └── Repositories/  (implementations)
// └── Presentation/
//     ├── Views/
//     └── ViewModels/

Benefits

Layered architecture enables independent testing, swappable implementations, and parallel team development.

// Test Domain in isolation: no network, no DB
// Swap RemoteUserRepo with LocalUserRepo for offline mode
// Teams own layers independently

Common Mistakes

Avoid importing UIKit/SwiftUI in the Domain layer, and avoid business logic leaking into views.

// WRONG: Domain entity with SwiftUI Color
struct Product {
  var color: Color  // UIKit/SwiftUI import in Domain!
}
// CORRECT: Use a plain string or enum
struct Product { var colorHex: String }

Quick Check

Which layer owns the repository protocols in Clean Architecture?

Lesson Recap

Clean Architecture layers: Domain (entities, use cases, protocol contracts), Data (network/DB implementations), Presentation (UI + ViewModels). Dependencies point inward toward Domain. Domain has zero framework dependencies, making it fully unit-testable.

Frequently asked questions

Is the “Layers: Domain, Data and Presentation” lesson free?

Yes — the full text of “Layers: Domain, Data and Presentation” is free to read here on the web, and the Swift Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Swift Academy course, upgrade to CoddyKit PRO.

What will I learn in “Layers: Domain, Data and Presentation”?

Separating business logic from data access and UI in a Swift app. You practise Swift Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Swift Academy?

No prior experience is required. Swift Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 1 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Layers: Domain, Data and Presentation” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Swift Academy lesson?

Yes. Every Swift Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Layers: Domain, Data and Presentation
  2. Use Case and Repository Patterns
  3. Dependency Inversion with Protocol Abstractions
  4. Wiring Layers Together without a DI Framework
← Back to Swift Academy