This is a modular and scalable iOS application built with Clean Architecture, SOLID principles, and modern Swift concurrency. The project demonstrates how to organize your app using feature-based modules (User), separating business logic, data handling, and presentation.
This project was created to help iOS developers structure real-world apps in a way that is testable, maintainable, and scalable. Whether you're working on a startup MVP or an enterprise-grade product, this example provides a clean and modular foundation.
- SwiftUI-based MVVM Presentation Layer
- Clean Architecture (Domain, Data, Presentation)
- Async/Await for networking
- Shared reusable APIService
- Decoupled Use Cases and Repositories
- Fully testable components
├── Modules
│ ├── User
│ │ ├── Data
│ │ │ └── Remote
│ │ │ └── UserRemoteDataSource.swift
│ │ ├── Domain
│ │ │ ├── Entities
│ │ │ │ └── User.swift
│ │ │ ├── Repositories
│ │ │ │ └── UserRepository.swift
│ │ │ └── UseCases
│ │ │ └── GetUsersUseCase.swift
│ │ └── Presentation
│ │ ├── ViewModels
│ │ │ └── UserListViewModel.swift
│ │ ├── Views (SwiftUI)
│ │ │ └── UserListViewModel.swift
│ │ └── ViewControllers (UIKit)
│ │ └── UserListViewController.swift
│ ├── New Module
├── Shared
│ └── Networking
│ └── APIService.swift
└── Tests
├── DomainTests
│ └── GetUsersUseCaseTests.swift
└── DataTests
└── APIServiceTests.swift
UserListViewdisplays a table view of usersUserListViewModelusesGetUsersUseCaseGetUsersUseCaseusesUserRepositoryUserRemoteDataSource(conforming to UserRepository) fetches users viaDefaultAPIService- Results or errors are passed back up to the view model and UI
This project includes unit tests for:
- Use Cases (e.g.
GetUsersUseCase) - APIService decoding using live endpoints
Run tests via:
⌘ + U (in Xcode)- Users API: https://jsonplaceholder.typicode.com/users
- Clone this repo
- Open
SampleCleanArchitecture.xcodeprojin Xcode - Run on simulator or device
- Xcode 14+
- iOS 15+
- Swift 5.7+
Copyright (c) 2025 Nouman Gul
This project demonstrates how to reuse the same UserListViewModel across SwiftUI and UIKit, allowing consistent business logic while maintaining separate UI layers.
let repo = UserRemoteDataSource()
let useCase = GetUsersUseCase(repository: repo)
let viewModel = UserListViewModel(getUsersUseCase: useCase)
UserListView(viewModel: viewModel)let repo = UserRemoteDataSource()
let useCase = GetUsersUseCase(repository: repo)
let viewModel = UserListViewModel(getUsersUseCase: useCase)
let userListVC = UserListViewController(viewModel: viewModel)- Promotes code reuse between UIKit and SwiftUI
- Keeps your business logic decoupled from the UI
- Ideal for gradual SwiftUI adoption or testing UI strategies side-by-side