A modern iOS application built with Clean Architecture principles, featuring a modular design with clear separation of concerns. The project demonstrates best practices for scalable, testable, and maintainable iOS development using Swift 6 and SwiftUI.
- Architecture Overview
- Project Structure
- Key Features
- Technology Stack
- Getting Started
- Module Dependencies
- Layer Responsibilities
- Data Flow
- Testing Strategy
- Best Practices
- Contributing
This project implements Clean Architecture (also known as Onion Architecture) with the following key principles:
- Dependency Rule: Dependencies only point inward. Inner layers know nothing about outer layers
- Separation of Concerns: Each layer has a specific responsibility
- Testability: Business logic is isolated and easily testable
- Independence: Business logic is independent of UI, Database, and external frameworks
- Reusability: Common UI components and utilities are shared across the application
┌─────────────────────────────────────────────────────┐
│ Presentation │
│ (ViewModels, Views, UI) │
├─────────────────────────────────────────────────────┤
│ Common │
│ (Shared UI Components, Extensions) │
├─────────────────────────────────────────────────────┤
│ Domain │
│ (Use Cases) │
├─────────────────────────────────────────────────────┤
│ Data Layer │
│ (Repositories, DTOs) │
├─────────────────────────────────────────────────────┤
│ Core │
│ (Entities, Protocols, Errors) │
└─────────────────────────────────────────────────────┘
Dependencies: ↓ (only inward)
Modules/
├── Common/ # Shared UI components & utilities
│ ├── Components/ # Reusable UI components
│ │ ├── CustomTextField
│ │ └── PrimaryButton
│ ├── Extensions/ # Swift & SwiftUI extensions
│ │ ├── View+Extensions
│ │ └── Date+Extensions
│ └── Styles/ # Custom styles & themes
│
├── Core/ # Business entities & contracts
│ ├── Entities/ # Business models
│ │ ├── UserEntity
│ │ └── SessionEntity
│ ├── Protocols/ # Repository interfaces
│ │ ├── AuthRepositoryProtocol
│ │ └── UserRepositoryProtocol
│ └── Errors/ # Domain errors
│ └── DomainError
│
├── Domain/ # Business logic
│ └── UseCases/ # Application use cases
│ └── Auth/ # Authentication use cases
│ ├── SignInUseCase
│ ├── SignOutUseCase
│ ├── GetCurrentUserUseCase
│ └── ObserveAuthStateUseCase
│
├── DataLayer/ # Data management
│ ├── Configuration/ # API configurations
│ │ └── SupabaseConfig
│ ├── Infrastructure/ # External services
│ │ └── SupabaseClientManager
│ ├── Repositories/ # Repository implementations
│ │ ├── AuthRepository
│ │ └── UserRepository
│ ├── DTOs/ # Data transfer objects
│ │ └── UserDTO
│ └── Mappers/ # DTO to Entity mappers
│ └── UserMapper
│
├── Presentation/ # UI layer
│ ├── ViewModels/ # Presentation logic
│ │ ├── AuthViewModel
│ │ └── UserViewModel
│ ├── Models/ # UI models
│ │ └── AlertItem
│ └── Views/ # SwiftUI Views
│ ├── ContentView
│ ├── LoginView
│ ├── HomeView
│ └── Components/
│ ├── ProfileTab
│ ├── StatsTab
│ └── SettingsTab
│
└── DI/ # Dependency injection
└── DIContainer.swift
- Clean Architecture: Strict separation of concerns with dependency inversion
- Modular Design: Independent, testable, and reusable modules
- SOLID Principles: Following Single Responsibility, Open/Closed, and other SOLID principles
- Type Safety: Full Swift 6 concurrency with Sendable protocol
- Authentication System: Complete auth flow with Supabase integration
- User Management: Profile management with subscription tracking
- Real-time Updates: Auth state observation with async/await
- Gamification: XP system, levels, and streak tracking
- Premium Features: Subscription management with RevenueCat integration
- Modern UI: Beautiful SwiftUI interface with gradients and animations
- Reusable Components: Common UI components library (CustomTextField, PrimaryButton)
- Tab Navigation: Intuitive navigation with Profile, Stats, and Settings tabs
- Dark Mode Support: Full dark mode compatibility
- Loading States: Elegant loading overlays and progress indicators
- Error Handling: User-friendly error alerts with AlertItem pattern
- Login Screen: Clean and modern sign-in interface with custom components
- Loading States: Elegant progress indicators during authentication
- Error Handling: User-friendly error messages with retry options
- Profile Tab: User information, stats grid, and achievement badges
- Stats Tab: Progress tracking, XP visualization, and leaderboards
- Settings Tab: Account management, preferences, and app configuration
- Custom gradients and shadows for depth
- Smooth animations and transitions
- Consistent spacing using 8-point grid
- Adaptive layouts for different screen sizes
- Language: Swift 6.2
- UI Framework: SwiftUI
- Minimum iOS: 17.0
- Architecture: Clean Architecture
- Backend: Supabase
- Package Manager: Swift Package Manager (SPM)
- Concurrency: Swift Concurrency (async/await)
- Reactive Programming: Combine Framework
- Xcode 16.0 or later
- iOS 17.0+ deployment target
- Swift 6.2
- Supabase account (for backend)
-
Clone the repository
git clone https://github.com/yourusername/your-project.git cd your-project -
Configure Supabase
Update
DataLayer/Sources/DataLayer/Configuration/SupabaseConfig.swift:// Development static let url = URL(string: "your-supabase-url")! static let anonKey = "your-anon-key"
-
Open in Xcode
open YourProject.xcodeproj
-
Build and Run
- Select your target device/simulator
- Press
Cmd + Rto build and run
For local Supabase development:
# Install Supabase CLI
brew install supabase/tap/supabase
# Start Supabase locally
supabase start
# The local credentials will be:
# URL: http://127.0.0.1:54321
# Anon Key: [provided by CLI]graph TD
App[App] --> DI[DI Container]
DI --> Presentation[Presentation]
DI --> Domain[Domain]
DI --> DataLayer[Data Layer]
DI --> Core[Core]
DI --> Common[Common]
Presentation --> Domain
Presentation --> Core
Presentation --> Common
Domain --> Core
DataLayer --> Core
DataLayer --> Supabase[Supabase SDK]
| Module | Dependencies | Description |
|---|---|---|
| Core | None | Contains business entities, protocols, and domain errors. The innermost layer with no dependencies. |
| Common | SwiftUI | Shared UI components, extensions, and utilities used across the presentation layer. |
| Domain | Core | Implements business logic through use cases. Contains application-specific business rules. |
| DataLayer | Core, Supabase | Handles data persistence, external services, and API communication. |
| Presentation | Core, Domain, Common | Contains ViewModels, Views, and UI-specific models. Manages UI state and user interactions. |
| DI | All modules | Manages dependency injection and object creation. Central configuration point for the app. |
- Purpose: Define business entities and contracts
- Key Components: UserEntity, SessionEntity, Repository Protocols
- Dependencies: None (purest layer)
- Purpose: Provide reusable UI components and utilities
- Key Components:
- CustomTextField: Styled text input component
- PrimaryButton: Consistent button styling with loading states
- View Extensions: Custom corner radius and UI helpers
- Date Extensions: Formatting utilities
- Dependencies: SwiftUI only
- Purpose: Encapsulate business logic
- Key Components:
- SignInUseCase: Authentication logic
- SignOutUseCase: Session termination
- GetCurrentUserUseCase: User retrieval
- ObserveAuthStateUseCase: Real-time auth monitoring
- Dependencies: Core
- Purpose: Implement data operations
- Key Components:
- AuthRepository: Authentication implementation
- UserRepository: User data management
- SupabaseClientManager: External service integration
- Dependencies: Core, Supabase SDK
- Purpose: Manage UI and user interaction
- Key Components:
- AuthViewModel: Authentication state management
- UserViewModel: User data presentation
- Views: LoginView, HomeView, ProfileTab, StatsTab, SettingsTab
- Dependencies: Core, Domain, Common
- Entities: Pure business models (UserEntity, SessionEntity)
- Protocols: Repository interfaces defining contracts
- Errors: Domain-specific error definitions
- Characteristics:
- No external dependencies
- Platform agnostic
- Highly stable
- UI Components: Reusable SwiftUI components
- CustomTextField: Consistent text input styling
- PrimaryButton: Standard button with loading states
- Extensions: Swift and SwiftUI extensions
- View modifiers and helpers
- Date formatting utilities
- Number formatting utilities
- Styles: Consistent theming and styling
- Characteristics:
- SwiftUI dependent
- Reusable across all UI
- Promotes consistency
- Use Cases: Encapsulates business rules
- Authentication flows
- User management
- State observation
- Business Rules: Application-specific logic
- Orchestration: Coordinates between data and presentation
- Characteristics:
- Platform and framework agnostic
- Testable in isolation
- Changes with business requirements
- Repository Implementations: Concrete implementations of Core protocols
- DTOs: Data Transfer Objects for external communication
- Mappers: Convert between DTOs and Entities
- External Service Integration:
- Supabase for backend
- RevenueCat for subscriptions
- Network communication
- Characteristics:
- Handles all I/O operations
- Manages caching strategies
- Deals with external APIs
- ViewModels: Presentation logic and state management
- AuthViewModel: Authentication state
- UserViewModel: User data presentation
- Views: SwiftUI user interface components
- LoginView: Authentication UI
- HomeView: Main application UI
- Tab Views: Profile, Stats, Settings
- UI Models: Models optimized for UI display
- AlertItem: Alert configuration
- Navigation state
- Characteristics:
- SwiftUI and Combine based
- Observes and reacts to state changes
- Handles user input
The Common module provides a rich set of reusable UI components:
CustomTextField(
placeholder: "Email",
text: $email,
isSecure: false
)- Consistent styling across the app
- Support for secure text entry
- Built-in padding and background
PrimaryButton(
title: "Sign In",
isLoading: viewModel.isLoading,
action: { await viewModel.signIn() }
)- Loading state with progress indicator
- Disabled state handling
- Consistent color scheme
cornerRadius(_:corners:): Apply corner radius to specific corners- Custom modifiers for consistent spacing
- Shadow and gradient helpers
timeAgoDisplay: Relative time formattingshortDate: Concise date displaydayOfWeek: Day name extraction
- Colors: Consistent color palette with dark mode support
- Typography: Standardized font sizes and weights
- Spacing: 8-point grid system
- Animations: Smooth transitions and loading states
User Input (View)
↓
AuthViewModel (Presentation)
↓
SignInUseCase (Domain)
↓
AuthRepository + UserRepository (Data)
↓
Supabase Client (External)
↓
Response mapping (DTO → Entity)
↓
Update ViewModel State
↓
UI Updates (View)
Each module includes its own test target:
// Domain Layer Test Example
class SignInUseCaseTests: XCTestCase {
func testSignInWithValidCredentials() async throws {
// Given
let mockAuthRepo = MockAuthRepository()
let mockUserRepo = MockUserRepository()
let useCase = SignInUseCase(
authRepository: mockAuthRepo,
userRepository: mockUserRepo
)
// When
let result = try await useCase.execute(
email: "test@example.com",
password: "password123"
)
// Then
XCTAssertNotNil(result.user)
XCTAssertEqual(result.user.email, "test@example.com")
}
}Test repository implementations with real backend:
class AuthRepositoryIntegrationTests: XCTestCase {
func testRealSignIn() async throws {
// Test with test database
}
}SwiftUI snapshot tests and UI flow tests:
class AuthFlowUITests: XCTestCase {
func testCompleteAuthFlow() throws {
// Test complete sign in → home → sign out flow
}
}Always inject dependencies rather than creating them:
// ✅ Good
class SignInUseCase {
init(authRepository: AuthRepositoryProtocol) { }
}
// ❌ Bad
class SignInUseCase {
init() {
self.authRepository = AuthRepository() // Direct instantiation
}
}Define contracts through protocols:
public protocol AuthRepositoryProtocol {
func signIn(email: String, password: String) async throws -> SessionEntity
}Use Common components for consistency:
// ✅ Good - Using Common components
CustomTextField(placeholder: "Email", text: $email)
PrimaryButton(title: "Submit", action: submit)
// ❌ Bad - Creating custom UI each time
TextField("Email", text: $email)
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(10)Separate presentation logic from views:
// ✅ Good - Logic in ViewModel
@MainActor
class AuthViewModel: ObservableObject {
func signIn(email: String, password: String) async {
// Handle business logic
}
}
// ❌ Bad - Logic in View
struct LoginView: View {
func signIn() async {
// Business logic in view
}
}Use domain-specific errors and user-friendly alerts:
// Domain layer
public enum DomainError: LocalizedError {
case invalidCredentials
case networkError(String)
}
// Presentation layer
struct AlertItem: Identifiable {
let title: String
let message: String
}Leverage Swift concurrency for asynchronous operations:
public func execute() async throws -> UserEntity {
try await repository.fetchUser()
}Ensure thread safety with Sendable:
public final class AuthRepository: AuthRepositoryProtocol, Sendable {
// Implementation
}Keep modules focused and independent:
// Each module has a single responsibility
// Core: Entities and contracts
// Domain: Business logic
// Common: Shared UI components
// DataLayer: External communication
// Presentation: User interface- API Keys: Never commit API keys. Use environment variables or configuration files
- Authentication: Implement proper token refresh mechanisms
- Data Validation: Always validate input data in use cases
- Error Messages: Don't expose sensitive information in error messages
- Lazy Loading: Load data only when needed
- Caching: Implement caching strategies in repositories
- Pagination: Use pagination for large data sets
- Image Optimization: Compress and cache images appropriately
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Follow Swift API Design Guidelines
- Use SwiftLint for code consistency
- Write self-documenting code with clear naming
- Add comments for complex logic
Follow conventional commits:
feat:New featurefix:Bug fixdocs:Documentation changesrefactor:Code refactoringtest:Test additions or updateschore:Maintenance tasks
This project is licensed under the MIT License - see the LICENSE file for details.
- Clean Architecture by Robert C. Martin
- Supabase for backend services
- Swift Community for best practices
- Author: Code toan bug
- Email: codetoanbug@gmail.com
- GitHub: @codetoanbug
⭐ If you find this project helpful, please consider giving it a star!



