How to Structure a SwiftUI Project in 2026
Starting a new SwiftUI project? The default Xcode structure works for tutorials, but real apps need something better. Here's what I use after building multiple production apps.
The Problem
Xcode gives you ContentView.swift and that's it. As your app grows, you end up with:
- 50 files in one folder
- ViewModels mixed with Views
- No clear separation of concerns
- Pain every time you need to find something
The Solution: Feature-Based Architecture
MyApp/
├── App/
│ ├── MyAppApp.swift
│ ├── AppDelegate.swift (if needed)
│ └── AppConfiguration.swift
├── Features/
│ ├── Auth/
│ │ ├── Views/
│ │ │ ├── LoginView.swift
│ │ │ └── RegisterView.swift
│ │ ├── ViewModels/
│ │ │ └── AuthViewModel.swift
│ │ └── Models/
│ │ └── User.swift
│ ├── Home/
│ │ ├── Views/
│ │ ├── ViewModels/
│ │ └── Models/
│ └── Profile/
│ ├── Views/
│ ├── ViewModels/
│ └── Models/
├── Core/
│ ├── Network/
│ │ ├── NetworkManager.swift
│ │ ├── APIEndpoint.swift
│ │ └── APIError.swift
│ ├── Storage/
│ │ ├── StorageManager.swift
│ │ └── KeychainManager.swift
│ └── Extensions/
│ ├── View+Extensions.swift
│ └── String+Extensions.swift
├── UI/
│ ├── Components/
│ │ ├── PrimaryButton.swift
│ │ ├── LoadingView.swift
│ │ └── ErrorView.swift
│ └── Theme/
│ ├── Colors.swift
│ ├── Fonts.swift
│ └── Spacing.swift
└── Resources/
├── Assets.xcassets
└── Localizable.strings
Why This Works
1. Features are Self-Contained
Each feature has everything it needs:
- Views (the UI)
- ViewModels (the logic)
- Models (the data)
Adding a new feature? Create a new folder. Removing one? Delete the folder.
2. Core is Shared Logic
Things used across features:
- Networking
- Storage
- Extensions
One place to maintain. Used everywhere.
3. UI is Reusable
Components and theming that every feature uses:
// UI/Theme/Colors.swift
import SwiftUI
enum AppColors {
static let primary = Color("Primary")
static let secondary = Color("Secondary")
static let background = Color("Background")
static let text = Color("Text")
}
// UI/Components/PrimaryButton.swift
struct PrimaryButton: View {
let title: String
let action: () -> Void
var body: some View {
Button(action: action) {
Text(title)
.font(.headline)
.foregroundColor(.white)
.frame(maxWidth: .infinity)
.padding()
.background(AppColors.primary)
.cornerRadius(12)
}
}
}
ViewModel Pattern
Every feature's ViewModel follows the same pattern:
@MainActor
final class HomeViewModel: ObservableObject {
// MARK: - Published State
@Published private(set) var items: [Item] = []
@Published private(set) var isLoading = false
@Published private(set) var error: Error?
// MARK: - Dependencies
private let networkManager: NetworkManager
init(networkManager: NetworkManager = .shared) {
self.networkManager = networkManager
}
// MARK: - Public Methods
func loadItems() async {
isLoading = true
error = nil
do {
items = try await networkManager.fetch(ItemsEndpoint())
} catch {
self.error = error
}
isLoading = false
}
}
Key points:
-
@MainActorensures UI updates on main thread -
private(set)prevents external mutation - Dependency injection for testing
Navigation
For simple apps, use SwiftUI's native navigation:
NavigationStack {
HomeView()
.navigationDestination(for: Item.self) { item in
DetailView(item: item)
}
}
For complex apps, consider a Coordinator:
@MainActor
final class AppCoordinator: ObservableObject {
@Published var path = NavigationPath()
enum Destination: Hashable {
case detail(Item)
case settings
case profile
}
func navigate(to destination: Destination) {
path.append(destination)
}
func pop() {
path.removeLast()
}
func popToRoot() {
path.removeLast(path.count)
}
}
Quick Setup Script
Create this structure automatically:
#!/bin/bash
mkdir -p App
mkdir -p Features/{Auth,Home,Profile}/{Views,ViewModels,Models}
mkdir -p Core/{Network,Storage,Extensions}
mkdir -p UI/{Components,Theme}
mkdir -p Resources
My Full Template
I've packaged all of this (plus actual code) into a starter template:
- 5 ready screens
- 20+ UI components
- NetworkManager with async/await
- Dark mode support
- MVVM architecture
Check it out at boosty.to/swiftuidev if you want to skip the setup.
TL;DR
- Feature-based structure — group by feature, not file type
- Core — shared utilities
- UI — reusable components and theming
-
MVVM — ViewModels with
@MainActor - Be consistent — same pattern everywhere
What's your project structure? Let me know in the comments.
Follow me for more SwiftUI content:
- Twitter: @peasee163
- Telegram: @SwiftUIDaily
Top comments (0)