Swift与MVVM架构新项目文件目录结构(ViewModels)最佳实践咨询
Hey there! I totally get where you're coming from—those generic type-based directories (Models/Views/ViewModels) work fine for tiny projects, but once your app scales to dozens of screens and features, they turn into a nightmare to navigate. Let me share what I've used in production Swift apps that stay maintainable even as teams and codebases grow.
Core Principle: Organize by Feature, Not by Type
Instead of lumping all ViewModels into one overcrowded folder, group related files (ViewModel, View, Model, even supporting logic) into feature-specific directories. This way, everything related to "Checkout" or "Profile" lives in one place—no more jumping between 5 different folders to make a small change to a single feature.
Example Production-Grade Directory Structure
Here’s a breakdown of a structure I’ve relied on for mid-to-large apps:
YourAppName/ ├─ Core/ # Shared, app-wide foundational logic │ ├─ Models/ # Cross-feature data models (e.g., User, BaseAPIResponse) │ ├─ ViewModels/ # Base view models (e.g., BaseViewModel with common Combine bindings/loading states) │ ├─ Services/ # Singleton/shared services (APIClient, AuthService, AnalyticsTracker) │ ├─ Helpers/ # Utilities (DateFormatter, ValidationRules, ImageCache) │ ├─ Extensions/ # SwiftUI/UIKit extensions (e.g., View+AppStyling, UIColor+BrandPalette) │ └─ Resources/ # Shared assets (colors, fonts, localized strings, common icons) ├─ Features/ # All feature-specific code (the heart of your app) │ ├─ Profile/ │ │ ├─ ProfileViewModel.swift │ │ ├─ ProfileView.swift (or ProfileViewController.swift for UIKit) │ │ ├─ ProfileModels.swift (feature-specific models, e.g., ProfileEditFormData) │ │ ├─ ProfileStore.swift (local state manager for the feature, if needed) │ │ └─ Components/ # Feature-specific subviews (e.g., ProfileHeaderView.swift) │ ├─ Checkout/ │ │ ├─ CheckoutViewModel.swift │ │ ├─ CheckoutView.swift │ │ ├─ CheckoutModels.swift │ │ ├─ CheckoutStore.swift │ │ └─ Components/ │ └─ Home/ │ ├─ HomeViewModel.swift │ ├─ HomeView.swift │ ├─ HomeModels.swift │ └─ Components/ ├─ Tests/ # Mirror the main structure for unit/integration tests │ ├─ CoreTests/ │ └─ FeaturesTests/ └─ UITests/
Why This Solves ViewModel Management Pain Points
- No more endless scrolling: Each feature’s ViewModel lives right next to its corresponding View and Model, making it trivial to trace logic flow.
- Encapsulation: A ViewModel tied exclusively to the Profile feature won’t pollute the global namespace or get mixed up with unrelated ViewModels.
- Team-friendly: Multiple developers can work on different features simultaneously without conflicting in a single ViewModels directory.
Pro Tips for ViewModel Maintenance
- Leverage base ViewModels: Create a
BaseViewModelinCore/ViewModelsthat handles repetitive tasks like loading state tracking, error handling, and Combine binding setup. All feature-specific ViewModels inherit from this to cut down on code duplication. - Avoid "God ViewModels": If a ViewModel grows beyond 300 lines, split it into smaller focused components. For example, a
HomeViewModelmight delegate feed logic to aHomeFeedManageror search logic to aHomeSearchHandler. - Pair ViewModels with feature-specific Stores: For complex state (like multi-step checkout), use a dedicated
Storewithin the feature folder. The ViewModel interacts with the Store to fetch/update state, keeping the ViewModel focused on presenting data to the View, not managing raw state.
When to Fall Back to Type-Based Directories
Type-based folders still have value for shared, app-wide code. For example, Core/Models holds models used across multiple features (like User), while feature-specific models stay in their respective feature folders.
Final Thought
The key is flexibility—adjust the structure to fit your team’s workflow and app’s complexity. If you have tons of shared UI components, add a SharedComponents folder under Core. The goal is to make it easy for any developer (new or veteran) to find code quickly and understand how parts of the app connect.
内容的提问来源于stack exchange,提问作者Erez Hod

