Skip to main content

πŸ“πŸ“ Folder Naming and File Structure

This DeepDive presents best practices for naming folders in iOS projects. Following these conventions helps maintain consistency, readability, and scalability, making projects easier to navigate and collaborate on.

General Folder Naming Guidelines​

  1. Use Descriptive and Contextual Folder Names
    Folder names should describe their contents clearly and provide context to avoid confusion. This is especially important for distinguishing between different layers like Views, Models, and Services within feature modules. Contextual naming also helps when folders are viewed outside the project, such as in code reviews or remote repositories.

  2. Use PascalCase for Core Feature or Domain Folders, Lowercase for Resource Folders.
    This distinction helps differentiate between business logic and static resources, making the folder structure more intuitive.

    • PascalCase is suitable for feature-related or domain-specific folders (e.g., UserProfile, LoginService, ProductDetails).
    • Use lowercase for resource-related folders, which often include assets, fonts, and other static resources (e.g., assets, fonts, localization).
  3. Singular vs. Plural Folder Names

    • Plural folder names should be used when the folder contains collections or multiple instances of something (e.g., Assets, Images, Resources).
    • Use singular names for folders that represent a single type of item, such as View, Model, or Service.
  4. Consistent Naming Across Features and Modules
    Folder names should be consistent across features, following a standard structure. For instance, if a feature folder contains View, Model, and Service subfolders, all feature folders should follow this pattern.

  5. Avoid Unnecessary Redundancy, But Use Context When Needed
    While folder names shouldn’t repeat unnecessary information, some redundancy can provide clarity, especially in nested folders. For example, within a UserProfile feature folder, subfolders like UserProfileViewModel.swift may provide necessary context.

  6. Flexibility in Folder Structure for Large Apps
    In larger iOS projects, the folder structure might evolve to accommodate complex feature modules, shared services, and cross-domain logic. Maintain a balance between standardization and flexibility to avoid over-complicating the project.

    • Core vs. Shared: Use clear boundaries between critical app logic (stored in Core) and reusable components (stored in Shared).
    • Features vs. Domain: Distinguish between feature-specific folders (e.g., Login, UserProfile) and domain-based folders for shared logic (e.g., User, Order).

/GroupByFeature/​

Organizing files by feature or module is an effective way to maintain scalability and modularity. In projects where features are independent (e.g., login, user profile, checkout), this approach encapsulates all feature-related components, such as views, models, view models, and services, within a specific folder.

Folder Naming Conventions in Feature-Based Organization​

NamePurpose
ViewContains UI components specific to the feature. Use singular View for a standard set of views. For large features with many views, you may subdivide views into more descriptive folders (e.g., FormView, DetailView).
ViewModelManages the presentation logic for the feature, typically following the MVVM architecture. If multiple view models are necessary, give them specific names based on their functionality (e.g., LoginFormViewModel.swift).
ServiceHandles networking, data fetching, or other external communication related to the feature.
ModelRepresents data structures used within the feature. For features with multiple models, group them into subfolders (e.g., RequestModels, ResponseModels).
ConstantsStores constants that are specific to the feature, such as API endpoints, keys, or static strings. If constants are used across features, consider moving them to Core.
Coordinator(Optional) If following the coordinator pattern, this folder contains navigation logic specific to the feature.
Repository(Optional) When using a repository pattern, handles the data layer logic, abstracting the data source.

Example Folder Structure for a Feature** (with Multiple Views and Models)

/Features/
/Login/
/View/
LoginFormView.swift
LoginSuccessView.swift
/ViewModel/
LoginFormViewModel.swift
/Service/
LoginAPIService.swift
/Model/
/RequestModels/
LoginRequestModel.swift
/ResponseModels/
LoginResponseModel.swift
/Constants/
LoginConstants.swift

When to Place Models or Constants Inside Feature Folders vs. Core/Shared Folders​

Deciding whether to store models, constants, or other resources in a feature folder or the Core/Shared folder depends on reusability and scope:

When to Keep Models and Constants Inside the Feature Folder:

  • Feature-Specific: If the model or constant is specific to the feature and unlikely to be reused by other features (e.g., LoginRequestModel, LoginResponseModel), keep it inside the feature folder.
  • Temporary Logic: If the logic or data model is related to a short-lived feature or specific implementation detail, it should stay in the feature folder to prevent cluttering the shared logic.

When to Move Models or Constants to Core/Shared:

  • Reusable Across Features: If the model or constant is used across multiple features, it should be placed in the Core/Shared folder. For instance, a UserModel that is referenced in both Login and UserProfile should go into the shared folder to avoid duplication.
  • Global Constants: Constants that are relevant across the entire app (e.g., API base URLs, common strings) should be placed in a shared constants file in Core/Shared rather than inside a specific feature folder.

Key Characteristics of Feature-Based Grouping:​

  1. Encapsulation: Components related to the same feature are grouped together, making the code easier to manage and navigate.
  2. Scalability: New features are easily added without affecting the structure of the existing codebase.
  3. Clarity and Modularity: Each feature is self-contained, making the app easier to test and modify without introducing unexpected bugs.
  4. Separation of Concerns: Each folder handles a specific aspect of the feature, ensuring clean separation of logic.

When to Avoid Feature-Based Grouping:​

  • Small Projects: If the app is small and has limited functionality, a feature-based grouping approach might introduce unnecessary complexity.
  • Highly Interdependent Features: If features are highly coupled and share a lot of business logic, a domain-based grouping may be more appropriate. We'll cover this in the Group by Domain section next.

/GroupByDomain/​

As apps grow in complexity, a Group by Domain approach is often better suited for maintaining scalability. Domains represent business areas or logical units (e.g., UserManagement, ProductCatalog, OrderProcessing) that contain multiple related features. Grouping by domain promotes clear separation of concerns and modular design by encapsulating everything related to a particular business domain.

Organizing Files Inside a Domain Folder​

Within each domain folder, you can organize files similarly to the Group by Feature approach. The key difference is that these folders contain cross-feature logic that might be used by several features related to that domain.

Common Folder Names in Domain-Based Grouping:

NamePurpose
ModelStores domain models that represent core entities in your app (e.g., User, Order, Product). These models are shared across features.
RepositoryContains repositories t hat abstract the data access layer, handling logic for fetching and storing data related to the domain.
ServiceHouses business logic services that are used across the domain. For example, an OrderService might be responsible for order validation and processing.
ViewModel(Optional) If the domain includes cross-feature view models, they can be stored here to manage shared presentation logic.
Coordinator(Optional) Contains coordinators that manage navigation logic for the domain.

Example Folder Structure for Domain-Based Organization:​

/Domain/
/UserManagement/
/Model/
User.swift
/Service/
UserService.swift
/Repository/
UserRepository.swift
/ProductCatalog/
/Model/
Product.swift
/Service/
ProductService.swift
/Repository/
ProductRepository.swift
/OrderProcessing/
/Model/
Order.swift
/Service/
OrderService.swift
/Repository/
OrderRepository.swift

When to Use Domain-Based Grouping​

Use Domain-Based Grouping When:

  1. Your App is Large or Complex: For apps with multiple cross-feature domains, such as an e-commerce app with domains like UserManagement, ProductCatalog, and OrderProcessing.
  2. Overlapping Logic Across Features: If multiple features share data models, services, or repositories, grouping by domain ensures that common logic is centralized.
  3. Business-Oriented Development: This approach works well for apps that reflect real-world business units (e.g., customers, products, orders), where each domain represents a major area of responsibility.

In an e-commerce app, you could have domains like ProductCatalog, OrderProcessing, and UserManagement. Each domain contains the logic that might be shared across multiple features (e.g., managing users, processing orders, or handling product inventory).

/Domain/
/ProductCatalog/
/Model/
Product.swift
/Service/
ProductService.swift
/Repository/
ProductRepository.swift
/OrderProcessing/
/Model/
Order.swift
/Service/
OrderService.swift
/UserManagement/
/Model/
User.swift
/Repository/
UserRepository.swift

Combining Domain-Based and Feature-Based Grouping​

In real-world projects, it’s common to combine Group by Domain and Group by Feature. This hybrid approach allows you to group simple features into isolated folders while centralizing shared logic in domain-based folders.

When to Combine Domain and Feature-Based Grouping:

  • Feature-Specific Components: Simple, isolated features like login can be grouped by feature, while larger areas of the app (e.g., product catalog or user management) can be organized by domain.
  • Shared Models and Services: Shared models and services should be placed in the domain folder to avoid duplication. Individual features can access these shared components as needed.

Example Structure Combining Features and Domains:

/Features/
/Login/
/View/
LoginView.swift
/ViewModel/
LoginViewModel.swift
/Service/
LoginAPIService.swift
/Model/
LoginRequestModel.swift
/Domain/
/UserManagement/
/Model/
User.swift
/Service/
UserService.swift
/Repository/
UserRepository.swift

Benefits of Domain-Based Grouping​

  1. Business-Oriented Structure: Reflects real-world business units or domains, making the codebase intuitive for developers familiar with the business logic.
  2. Encapsulation of Shared Logic: Centralizes logic, models, and services that are shared across multiple features, avoiding duplication.
  3. Modularity and Scalability: Promotes modularity, as each domain can grow independently, allowing teams to work on different domains without affecting others.

When to Avoid Domain-Based Grouping​

  • Small Projects: For smaller apps with limited complexity, domain-based grouping can introduce unnecessary overhead and complexity.
  • Feature-Specific Logic: If a feature is very specific to one part of the app and doesn’t share much with other parts, it may be better to use a feature-based approach to avoid overcomplicating the structure.

/Core/​

The Core folder contains the essential, business-critical components of your app. This folder centralizes functionality related to data handling, services, domain logic, and app-wide configurations. The Core folder is designed to encapsulate the app’s core functionality, ensuring that the app can scale, be maintained easily, and remain organized.

/Data/​

The Data folder manages all aspects of data handling within the app. This includes external API communication, local data persistence (via CoreData or other databases), and services for interacting with remote or local data sources. The goal is to centralize data-related logic so that the business logic can be cleanly separated from data retrieval or storage.

Breakdown:​

/Data/
/API/
APIClient.swift
APIRequest.swift
APIConfiguration.swift
APIParser.swift
/Services/
AuthService.swift
SessionManager.swift
LocationService.swift
/Repositories/
UserRepository.swift
ProductRepository.swift
/Model/
User.swift
Product.swift
Order.swift
/LocalData/
/CoreData/
CoreDataManager.swift
CoreDataMigrations.swift
/Caching/
DiskCache.swift
MemoryCache.swift

Explanation:​

  1. /API/:

    • Purpose: Centralizes all logic related to API communication, including sending requests, parsing responses, and managing API-specific configurations (e.g., authentication tokens, base URLs).
    • What goes here:
      • APIClient.swift: Manages network requests and handles retry logic, error handling, and response parsing.
      • APIRequest.swift: Defines the structure of API requests, specifying endpoints, parameters, and headers.
      • APIConfiguration.swift: Stores global API configuration such as base URLs, default headers, and token management.
      • APIParser.swift: Handles parsing raw API responses (e.g., JSON or XML) into usable models or error states.
    • When to simplify: For simpler apps, you might combine APIRequest, APIParser, and APIConfiguration into a single API class if the complexity doesn’t warrant separate files.
  2. /Services/:

    • Purpose: Contains business-critical services that are essential for the app's functionality, such as user authentication, session management, or location services.
    • What goes here:
      • AuthService.swift: Handles user authentication logic (e.g., login, logout, token refresh).
      • SessionManager.swift: Manages user sessions, handling token storage, expiration checks, and user state.
      • LocationService.swift: Manages the collection and processing of location data, using services like CoreLocation.
    • When to simplify: If your app only has one or two core services, you might consolidate them into a single Services.swift file to reduce file bloat.
  3. /Repositories/:

    • Purpose: Manages data retrieval and persistence, abstracting the underlying data sources (e.g., APIs, databases) from the app’s business logic. Repositories provide a clear interface for the app to fetch, store, or update data.
    • What goes here:
      • UserRepository.swift: Provides an abstraction over user-related data, handling operations like user creation, retrieval, and updates.
      • ProductRepository.swift: Manages data access related to products, such as fetching product details, searching products, or storing product information.
    • When to simplify: For smaller apps that only interact with one type of data source (e.g., an API), you may not need separate repositories. Instead, data-fetching logic could be integrated directly into services under API or Data.
  4. /Model/:

    • Purpose: Stores data models that represent the core entities in the app, such as users, products, and orders. These models are typically business-critical and serve as the foundation of your app’s data structures.
    • What goes here:
      • User.swift: The data model for user-related information.
      • Product.swift: The data model for representing a product.
      • Order.swift: Represents orders or transactions in the app.
    • When to simplify: If your app has very few models, you can group all models into a single Models.swift file rather than splitting them into individual files.
  5. /LocalData/:

    • Purpose: Contains logic for managing local persistence, including databases (e.g., CoreData) and caching strategies for offline storage or performance improvement.
    • What goes here:
      • CoreDataManager.swift: Manages CoreData stack setup, CRUD operations, and context handling.
      • CoreDataMigrations.swift: Handles database migrations in CoreData.
      • DiskCache.swift: Implements disk-based caching for storing large files or frequently used data.
      • MemoryCache.swift: Implements in-memory caching to speed up access to data that is fetched repeatedly.
    • When to simplify: If the app only uses one form of local persistence (e.g., just CoreData), you may not need separate caching files or Database folders.

/Domain/​

The Domain folder contains the business logic of the app, including domain-specific models, mappers, and repositories. This folder isolates how core entities in your appβ€”like users or productsβ€”are managed, separated from the data handling in the Data folder. It defines the rules, processes, and workflows that drive the app’s behavior.

Breakdown:​

/Domain/
/Model/
User.swift
Product.swift
Order.swift
/Mappers/
UserMapper.swift
ProductMapper.swift
/Repositories/
UserRepository.swift
ProductRepository.swift

Explanation:​

  1. /Model/:

    • Purpose: Represents domain-specific models, which reflect the business logic of your app. These models define how core entities (like users, orders, products) are structured and managed.
    • What goes here:
      • User.swift: Defines the user data model, including business-specific attributes (e.g., account types, roles).
      • Product.swift: Represents a product entity with business-specific attributes like pricing, availability, or categories.
      • Order.swift: Manages order data, including relationships between users and products, order status, etc.
  2. /Mappers/:

    • Purpose: Converts data models (e.g., API models) into domain models or vice versa. This layer ensures that the domain models used in business logic are decoupled from the underlying data representation (e.g., from the API or database).
    • What goes here:
      • UserMapper.swift: Converts API or database representations of a user into a domain model.
      • ProductMapper.swift: Converts product data between API, persistence, and domain layers.
  3. /Repositories/:

    • Purpose: Provides an abstraction over the domain’s business logic and ensures that data access (e.g., reading or writing data) is centralized and decoupled from the rest of the app.
    • What goes here:
      • UserRepository.swift: Interfaces with services or data sources to provide business-specific data access for users.
      • ProductRepository.swift: Manages the data access logic for products.

/Utils/​

The Utils folder contains general-purpose utility classes, extensions, and helpers that are shared across the app. These utilities provide common functionality but aren’t tied to any specific feature or business logic. They improve the maintainability of your app by abstracting away repetitive code.

Breakdown:​

/Utils/
/Extensions/
StringExtensions.swift
DateExtensions.swift
/Helpers/
ErrorHandler.swift
DateFormatterHelper.swift
/Formatters/
CurrencyFormatter.swift
NumberFormatter.swift

Explanation:​

  1. /Extensions/:

    • Purpose: Stores extensions that add methods to Swift’s built-in types or UIKit components to simplify repetitive tasks.
    • What goes here:
      • StringExtensions.swift: Provides utilities for string operations like trimming whitespace, validating email addresses, or formatting phone numbers.
      • DateExtensions.swift: Adds functionality to the Date type, such as calculating differences between dates or converting to strings.
  2. /Helpers/:

    • Purpose: Contains utility classes that help with common tasks like error handling, date formatting, or file operations.
    • What goes here:
      • ErrorHandler.swift: Centralized error handling logic, such as logging errors or displaying error messages.
      • DateFormatterHelper.swift: A helper class that ensures consistent date formatting across the app.
  3. /Formatters/:

    • Purpose: Contains reusable formatting logic for presenting data, such as currency values or numbers.
    • What goes here:
      • CurrencyFormatter.swift: Formats currency values in a localized format.
      • NumberFormatter.swift: Formats numbers (e.g., decimals, percentages) based on user locale or business requirements.

/Config/​

The Config folder stores app-wide configuration and environment-specific settings. This folder includes settings like API keys, base URLs, and environment-specific configurations (e.g., for development, staging, production). Centralizing this logic ensures that environment changes are easy to manage without affecting the app’s core logic.

Breakdown:​

/Config/
/Environments/
Development.xcconfig
Staging.xcconfig
Production.xcconfig
/FeatureFlags/
FeatureFlagManager.swift
FeatureToggles.swift
/AppSettings/
AppSettingsManager.swift

Explanation:​

  1. /Environments/:

    • Purpose: Stores environment-specific configuration files, managing settings for development, staging, and production environments.
    • What goes here:
      • Development.xcconfig: Config file for the development environment.
      • Staging.xcconfig: Config file for the staging environment.
      • Production.xcconfig: Config file for the production environment.
  2. /FeatureFlags/:

    • Purpose: Contains logic for managing feature flags, which allow features to be toggled on or off across different environments.
    • What goes here:
      • FeatureFlagManager.swift: Manages the logic for enabling or disabling features based on configuration settings.
      • FeatureToggles.swift: Stores the actual feature flags and their states (enabled/disabled).
  3. /AppSettings/:

    • Purpose: Centralized management of app-wide settings, such as user preferences, theme settings, or notification settings.
    • What goes here:
      • AppSettingsManager.swift: A class that manages app-wide settings (e.g., dark mode, language preferences).

/AppServices/​

The AppServices folder houses app-wide services that manage critical background processes or system interactions. These services are used throughout the app but are not tied to specific features.

Breakdown:​

/AppServices/
SessionService.swift
AuthService.swift
PushNotificationService.swift
PermissionsService.swift

Explanation:​

  1. SessionService.swift:

    • Purpose: Manages the user’s session, including login state, token storage, and session expiration.
    • What goes here: Logic for persisting, updating, and invalidating user sessions.
  2. AuthService.swift:

    • Purpose: Handles user authentication, including login, logout, and token management.
    • What goes here: Logic for authenticating users, refreshing tokens, and logging out.
  3. PushNotificationService.swift:

    • Purpose: Manages push notifications, handling device registration, notification payloads, and user interactions with notifications.
    • What goes here: Logic for registering devices with the push notification service and managing notification actions.
  4. PermissionsService.swift:

    • Purpose: Manages app permissions for accessing device services like location, camera, and notifications.
    • What goes here: Logic for requesting, checking, and managing user permissions.

/Shared/​

The Shared folder contains reusable components, extensions, utilities, and helpers that are not tied to specific business logic but are used across multiple features or modules of the app. The purpose of this folder is to centralize common, reusable functionality, ensuring that the codebase is DRY (Don't Repeat Yourself) and easy to maintain.

Unlike Core, which houses critical, business-centric logic, Shared focuses on general-purpose tools and utilities that support the development process across the entire app.

This section provides a detailed breakdown of the Shared folder and explains what kinds of components belong here and how they should be structured.

Best Practices for Shared Folder​

  • Decoupled from Business Logic: Ensure that the Shared folder only contains generic components that do not depend on business logic or app-specific domains. Anything tied to business logic should live in the Core folder.

  • Avoid Over-Nesting: Keep the folder structure relatively flat. Avoid creating deep folder hierarchies, which can lead to complexity and make components harder to find.

  • Organized by Functionality: Group reusable components by their function (e.g., Helpers, Views, Utilities) to ensure the organization is intuitive for any developer working on the project.

  • Feature-Agnostic: Components in Shared should not depend on any feature-specific logic. For example, a CustomButton should be customizable for different contexts without being tied to a specific screen or feature.

  • Be Consistent: Ensure that naming conventions and file structure in Shared are consistent with the rest of the project. This consistency makes it easier to navigate and reuse components across the app.

/Components/​

The Components folder contains reusable UI elements that can be shared across various views or features in the app. These components are typically generic and customizable, making them flexible for use across different parts of the app.

Breakdown:​

/Components/
CustomButton.swift
RoundedTextField.swift
LoadingSpinner.swift

Explanation:​

  1. CustomButton.swift:

    • Purpose: A customizable button component that can be reused in multiple views throughout the app, featuring consistent styling and behavior.
    • What goes here: Logic for button appearance (e.g., rounded corners, shadows) and interactions (e.g., animations on tap, custom actions).
  2. RoundedTextField.swift:

    • Purpose: A reusable input field with rounded edges that can be used in various forms, from login screens to search bars.
    • What goes here: Customizable properties like border styles, placeholder text configuration, and validation methods.
  3. LoadingSpinner.swift:

    • Purpose: A reusable spinner that can be shown during data loading or other asynchronous operations to indicate progress to the user.
    • What goes here: The logic for displaying and hiding the loading spinner, as well as any custom animations or color themes.

    When to simplify: If the app only has a few custom UI components, you can consolidate smaller components into a UIComponents.swift file to reduce the number of individual files.

/Extensions/​

The Extensions folder contains extensions to native Swift types and UIKit components that provide additional functionality or helper methods. These extensions are used across the app to make working with built-in types easier and more consistent.

Breakdown:​

/Extensions/
StringExtensions.swift
DateExtensions.swift
UIColorExtensions.swift
UIViewExtensions.swift

Explanation:​

  1. StringExtensions.swift:

    • Purpose: Provides utility methods for working with String types, such as trimming whitespace, checking for valid email formats, or converting strings to other types.
    • What goes here: Common string manipulation methods, such as trim(), isValidEmail(), or toInt().
  2. DateExtensions.swift:

    • Purpose: Adds helper methods to the Date class to simplify working with date and time operations, such as formatting, calculating time intervals, and comparisons.
    • What goes here: Methods for date formatting (toString()), time calculations (addDays()), or converting Date to different formats.
  3. UIColorExtensions.swift:

    • Purpose: Adds convenience methods for working with UIColor, such as creating colors from hex values or predefined app colors.
    • What goes here: Methods like fromHex() to create colors from hex codes or static properties for common theme colors like appPrimaryColor.
  4. UIViewExtensions.swift:

    • Purpose: Adds utility methods to the UIView class, such as adding shadows, rounding corners, or performing animations.
    • What goes here: Common UI tweaks, like addRoundedCorners(), addShadow(), or animateFadeIn().

    When to simplify: If there are only a few extensions, you can combine them into a TypeExtensions.swift file to avoid scattering too many files.

/Helpers/​

The Helpers folder contains utility classes and functions that provide common functionality across the app, such as handling dates, errors, or storing user settings. These helpers simplify complex or repetitive tasks, making the development process more efficient.

Breakdown:​

/Helpers/
DateFormatterHelper.swift
UserDefaultsHelper.swift
ErrorHandler.swift
FileHelper.swift

Explanation:​

  1. DateFormatterHelper.swift:

    • Purpose: Centralizes logic for formatting dates across the app, ensuring consistent date representations when displaying or parsing date values.
    • What goes here: Methods for formatting dates to different string formats (e.g., formatToLongDate(), formatToShortTime()), ensuring consistency across the app.
  2. UserDefaultsHelper.swift:

    • Purpose: Simplifies interaction with UserDefaults, providing a clear and reusable way to store and retrieve user preferences or small amounts of persistent data.
    • What goes here: Helper methods for reading and writing to UserDefaults (e.g., savePreference() and getPreference()), making the process more readable and less error-prone.
  3. ErrorHandler.swift:

    • Purpose: Provides a centralized way to handle and report errors across the app, ensuring consistent error handling logic.
    • What goes here: Methods to log errors, display user-friendly error messages, or even send error reports to a backend (e.g., handleError(), logError()).
  4. FileHelper.swift:

    • Purpose: Handles file management tasks, such as reading and writing files to disk, managing file paths, and deleting files.
    • What goes here: Methods for creating, reading, updating, and deleting files (e.g., saveFile(), readFile(), deleteFile()).

    When to simplify: If the app has a minimal amount of helper logic, you can merge similar helpers into a single UtilityHelpers.swift file.

/Formatters/​

The Formatters folder contains reusable logic for formatting data types like numbers, currencies, and dates into human-readable formats. These formatters ensure that the presentation of data across the app is consistent and localized.

Breakdown:​

/Formatters/
CurrencyFormatter.swift
NumberFormatter.swift
PercentageFormatter.swift

Explanation:​

  1. CurrencyFormatter.swift:

    • Purpose: Provides a centralized way to format monetary values across the app, ensuring consistency in the display of currencies.
    • What goes here: Methods for formatting numbers into currency strings based on locale (e.g., formatToCurrency()).
  2. NumberFormatter.swift:

    • Purpose: Ensures that numbers are formatted consistently throughout the app, whether they represent decimal points, integers, or larger numbers.
    • What goes here: Methods for formatting numbers into localized strings (e.g., formatToDecimal(), formatToInteger()).
  3. PercentageFormatter.swift:

    • Purpose: Formats numbers as percentages, ensuring that percentage values are presented consistently and with the appropriate number of decimal places.
    • What goes here: Methods to format a number as a percentage string (e.g., formatToPercentage()).

/Views/​

The Views folder contains reusable custom views, which can include table view cells, headers, footers, and other UI components that are not tied to specific features but are used across the app.

Breakdown:​

/Views/
CustomTableViewCell.swift
SectionHeaderView.swift
ReusableFooterView.swift
ProfileImageView.swift

Explanation:​

  1. CustomTableViewCell.swift:

    • Purpose: A reusable table view cell that is designed with a specific layout or style, making it adaptable to multiple screens and contexts.
    • What goes here: Logic for configuring table view cells with specific designs, such as labels, images, and accessory views.
  2. SectionHeaderView.swift:

    • Purpose: A customizable header view that can be used to label sections in table views or collection views, providing a consistent appearance.
    • What goes here: Layout and styling for section headers, such as titles, icons, or buttons.
  3. ReusableFooterView.swift:

    • Purpose: A reusable footer view for displaying summary information or actions at the bottom of lists or views.
    • What goes here: Custom logic and layout for footers, such as buttons or labels, depending on the use case.
  4. ProfileImageView.swift:

    • Purpose: A reusable image view component for displaying user profile images across various screens.
    • What goes here: Logic for loading and displaying profile images, including placeholder management and image scaling.

/Utilities/​

The Utilities folder contains more generalized, low-level utilities that are used throughout the app but aren't tied to any specific feature. These utilities can include caching, parsing, or other helper classes that streamline data processing or storage.

Breakdown:​

/Utilities/
JSONParser.swift
ImageCache.swift
NetworkReachability.swift
FileDownloader.swift

Explanation:​

  1. JSONParser.swift:

    • Purpose: Centralizes the logic for parsing JSON data into Swift objects, ensuring consistency in how API responses are handled.
    • What goes here: Methods for decoding JSON into models (e.g., decode()) or encoding Swift objects into JSON (e.g., encode()).
  2. ImageCache.swift:

    • Purpose: Manages caching of images to improve performance by avoiding repeated downloads of the same image.
    • What goes here: Logic for caching images in memory or on disk, along with retrieval methods to access cached images.
  3. NetworkReachability.swift:

    • Purpose: Provides utility methods to check network connectivity, ensuring that the app responds appropriately to changes in network status.
    • What goes here: Methods to check for network availability, detect connection type (e.g., Wi-Fi or cellular), and listen for network changes.
  4. FileDownloader.swift:

    • Purpose: Simplifies the process of downloading files from the web, including support for resumable downloads and error handling.
    • What goes here: Methods for downloading files, handling errors, and managing progress.

/Resources/​

The Resources folder contains static assets such as images, fonts, storyboards, and localization files. These resources are essential for maintaining the app’s UI consistency and localization capabilities.

/Assets.xcassets/​

The Assets folder manages image assets, app icons, and color sets. Xcode’s Assets.xcassets catalog optimizes the app’s assets, ensuring the right image and resolution are used for different devices.

Breakdown:​

/Assets.xcassets/
/AppIcons/
AppIcon.appiconset
/LaunchImages/
LaunchImage.imageset
/Icons/
IconLogin.imageset
IconUser.imageset
/Backgrounds/
BackgroundHome.imageset
BackgroundProfile.imageset
/Colors/
ThemeColors.colorset

Explanation:​

  1. AppIcons:

    • Purpose: Contains the app's icons, including all required resolutions for various device sizes.
    • What goes here: App icons, managed by Xcode automatically. No manual edits are required.
  2. LaunchImages:

    • Purpose: Stores launch screen images for different devices and screen sizes.
    • What goes here: Launch images that appear when the app is first opened.
  3. Icons:

    • Purpose: Contains smaller icons, such as tab bar icons, navigation icons, or button icons.
    • What goes here: Icons used across the app, categorized by their usage (e.g., Login, Profile).
  4. Backgrounds:

    • Purpose: Stores background images used in different parts of the app, such as home screen or profile screen backgrounds.
    • What goes here: Background images, organized by screen or section of the app.
  5. Colors:

    • Purpose: Holds custom color sets for theming and consistent color usage across the app.
    • What goes here: Color assets that define the app's primary, secondary, or accent colors.
Asset TypePurposeWhat goes here
AppIconsContains the app's icons, including all required resolutions for various device sizes.App icons, managed by Xcode automatically. No manual edits are required.
LaunchImagesStores launch screen images for different devices and screen sizes.
IconsContains smaller icons, such as tab bar icons, navigation icons, or button icons, categorized by their usage.
BackgroundsStores background images used in different parts of the app, organized by screen or section.
ColorsHolds custom color sets for theming and consistent color usage across the app, defining primary, secondary, or accent colors.

Best Practices:​

  • Use Assets.xcassets for all image assets and color sets to optimize the app for different device resolutions.
  • Categorize icons and images by their purpose (e.g., icons, backgrounds) to make it easier to find and update specific assets.

/Storyboards/​

The Storyboards folder contains the app’s UI layout and navigation logic. Each feature or module can have its own storyboard, defining its view controllers and transitions between views.

Breakdown:​

/Storyboards/
Main.storyboard
Login.storyboard
UserProfile.storyboard

Explanation:​

  1. Main.storyboard:

    • Purpose: Typically serves as the main entry point for the app’s UI. Contains the root navigation and primary view controllers.
    • What goes here: The initial views and navigation logic of the app.
  2. Feature-Specific Storyboards:

    • Purpose: Each major feature or module can have its own storyboard to encapsulate its UI components, reducing complexity in the main storyboard.
    • What goes here: Feature-specific view controllers and transitions, such as login screens or profile views.

Best Practices:​

  • Modularize your UI by breaking it down into feature-specific storyboards. This helps keep storyboards manageable and reduces merge conflicts in larger teams.
  • Use Main.storyboard as a central navigation point but avoid overloading it with complex UI logic.

/Fonts/​

The Fonts folder stores custom fonts used across the app. Fonts should be organized by font family and variations (e.g., bold, italic, regular), ensuring easy access and management.

Breakdown:​

/Fonts/
/CustomFont/
CustomFont-Regular.ttf
CustomFont-Bold.ttf
/AnotherFont/
AnotherFont-Light.ttf
AnotherFont-Medium.ttf

Explanation:​

  1. CustomFont/:

    • Purpose: Contains the regular and bold variations of the custom font used in the app.
    • What goes here: All variations of the font family, ensuring consistent typography across the app.
  2. AnotherFont/:

    • Purpose: Another custom font used in the app, organized by variations (light, medium, bold).
    • What goes here: All font files related to this family.

Best Practices:​

  • Organize fonts by font family, with subfolders for each family containing all variations.
  • Ensure that fonts are properly registered in the app’s Info.plist to be used across the app.

/Localization/​

The Localization folder contains .strings files for supporting multiple languages. Each language has its own subfolder, allowing the app to display localized content based on the user’s settings.

Breakdown:​

/Localization/
/en.lproj/
Main.strings
Login.strings
/es.lproj/
Main.strings
Login.strings

Explanation:​

  1. en.lproj:

    • Purpose: Contains English (en) localization strings for the app’s UI and features.
    • What goes here: .strings files with localized text for various parts of the app (e.g., main UI, login feature).
  2. es.lproj:

    • Purpose: Stores the Spanish (es) localization files, allowing the app to support Spanish-speaking users.
    • What goes here: Spanish translations of the app’s UI text and error messages.

Best Practices:​

  • Organize localization by feature: Separate strings files by features or modules (e.g., Login.strings, Profile.strings) to keep files manageable.
  • Always use NSLocalizedString to ensure all user-facing text is properly localized.

/Sounds/​

The Sounds folder contains sound effects and background music used in the app. This includes notification sounds, UI feedback sounds, and any music files.

Breakdown:​

/Sounds/
NotificationSound.aiff
BackgroundMusic.mp3

Explanation:​

  1. NotificationSound.aiff:

    • Purpose: A custom sound used for push or in-app notifications.
    • What goes here: Notification sound files in a format optimized for mobile use.
  2. BackgroundMusic.mp3:

    • Purpose: Background music for the app or specific sections like home screens or game levels.
    • What goes here: Music files optimized for app performance and user experience.

Best Practices:​

  • Optimize file formats: Use compressed formats like .mp3 for background music to reduce file size.
  • Organize sound files by type (e.g., notification sounds, background music) to maintain clarity.

/Config/​

The Config folder is responsible for environment-specific settings and build-time configurations. It centralizes configurations that affect how the app behaves across different environments (e.g., development, staging, production).

Folders:​

/Config/
/Environments/
/Plist/
/FeatureFlags/

Breakdown:​

  1. /Environments/:

    • Purpose: Stores build-time settings for different environments (development, staging, production).
    • Contents:
      • .xcconfig files for API base URLs, API keys, and environment-specific settings.
      • Example: Development.xcconfig, Production.xcconfig.
  2. /Plist/:

    • Purpose: Contains separate Info.plist files for each environment to define environment-specific app settings (e.g., bundle identifiers, permissions).
    • Example: Development-Info.plist, Production-Info.plist.
  3. /FeatureFlags/:

    • Purpose: Manages build-time feature flags to enable or disable features based on the environment.
    • Contents: FeatureFlagManager.swift to toggle features for different builds (e.g., beta features).

/Tests/​

The Tests folder is dedicated to ensuring that your app functions as expected by housing all unit tests, UI tests, and mocks. A well-structured test folder is essential for maintaining clarity, scalability, and ease of debugging, especially when the test suite grows large (e.g., 100+ tests).


/AppNameTests/​

The AppNameTests folder contains unit tests for testing business logic, models, and services. Unit tests focus on verifying individual components of the app in isolation. As your app grows, you’ll need a structure that scales and is easy to maintain.

Breakdown:​

/AppNameTests/
/CoreTests/
/API/
APIClientTests.swift
APIRequestTests.swift
/Services/
UserServiceTests.swift
AuthServiceTests.swift
/Repositories/
UserRepositoryTests.swift
ProductRepositoryTests.swift
/FeatureTests/
/Login/
LoginViewModelTests.swift
LoginServiceTests.swift
/UserProfile/
UserProfileViewModelTests.swift
UserProfileServiceTests.swift

Explanation:​

  1. CoreTests:

    • Purpose: Tests for core services, repositories, and shared logic used across the app.
    • What goes here: Unit tests for services like APIClient and repositories like UserRepository or ProductRepository.
    • Example: APIClientTests.swift tests networking logic, while UserRepositoryTests.swift verifies user data handling.
  2. FeatureTests:

    • Purpose: Tests for specific features or modules of the app (e.g., Login, User Profile).
    • What goes here: Tests for feature-specific logic, such as view models and feature services.
    • Example: LoginViewModelTests.swift ensures the login view model works correctly, while UserProfileServiceTests.swift tests the user profile service.

Scaling Strategy for Large Projects:​

  • Group by Feature: For large apps, group tests by feature or domain (e.g., Login, UserProfile) to keep tests organized as the test count increases.
  • Separate Core Logic: Keep core logic (like API services, repositories, etc.) in separate CoreTests to avoid mixing them with feature-specific tests.
  • Use Small, Isolated Tests: Each test should focus on one behavior or aspect of a function to keep tests manageable and easy to debug.

/AppNameUITests/​

The AppNameUITests folder contains tests for validating the app’s user interface by simulating user interactions. UI tests ensure that the app’s interface works correctly under various conditions, such as button presses, form submissions, and screen transitions.

Breakdown:​

/AppNameUITests/
/Login/
LoginUITests.swift
/UserProfile/
UserProfileUITests.swift
/Checkout/
CheckoutFlowUITests.swift
/ProductCatalog/
ProductCatalogUITests.swift

Explanation:​

  1. LoginUITests.swift:

    • Purpose: Tests the login screen’s UI, ensuring that buttons, input fields, and navigation behave correctly.
    • What goes here: Tests for the login flow, such as validating input, handling errors, and successful logins.
  2. UserProfileUITests.swift:

    • Purpose: Tests the user profile’s UI, verifying interactions like updating profile information or uploading a profile picture.
    • What goes here: Tests for interactions with the user profile, ensuring the correct data is displayed and updated.
  3. CheckoutFlowUITests.swift:

    • Purpose: Tests the checkout flow, including adding items to the cart, payment processing, and order confirmation.
    • What goes here: UI tests that simulate the entire checkout process, ensuring smooth navigation between screens.

Scaling Strategy for Large Projects:​

  • Group by User Flow or Feature: Just like in unit tests, organize UI tests by feature (e.g., Login, Checkout). This helps testers find and run relevant tests more easily.
  • Modular UI Tests: Break UI tests into smaller, manageable tests for specific actions (e.g., testAddToCart(), testCheckoutWithValidPayment()).
  • Run Tests in Parallel: For large UI test suites, consider running tests in parallel to speed up test execution.

/Mocks/​

The Mocks folder contains mock objects used to simulate external dependencies such as network services, databases, or external APIs. Mocks are essential for isolating the logic under test, ensuring that external factors do not interfere with the tests.

Breakdown:​

/Mocks/
/API/
MockAPIClient.swift
MockProductService.swift
/Services/
MockAuthService.swift
MockUserService.swift
/Repositories/
MockUserRepository.swift
MockOrderRepository.swift

Explanation:​

  1. MockAPIClient.swift:

    • Purpose: Simulates network requests, returning predefined responses for API calls.
    • What goes here: Mock implementations of API requests to simulate success, failure, and edge cases in networking.
  2. MockAuthService.swift:

    • Purpose: Provides fake authentication logic for testing login and logout flows.
    • What goes here: Mocked authentication methods to simulate valid and invalid login attempts.
  3. MockUserRepository.swift:

    • Purpose: Simulates interactions with a user repository or database, providing fake user data.
    • What goes here: Mock methods for fetching, updating, and deleting user data.

Scaling Strategy for Large Projects:​

  • Reusable Mocks: Write reusable mock objects that can be shared across multiple tests, avoiding duplication of logic.
  • Organize Mocks by Type: Keep mocks for APIs, services, and repositories in separate folders, ensuring they are easy to find and reuse.