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 + R
to 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!