CurrencyExchange is a simple iOS app that allows users to convert an amount from one currency to another. The app features a single screen where users can select source and target currencies using a picker, enter an amount to be exchanged, and view the converted amount. The app also implements a polling mechanism that refreshes the exchange rate every 15 seconds when the user is not actively editing.
The app is built using UIKit for the UI and uses programatic UI, Combine for reactive programming, and Swift Concurrency (async/await) for modern asynchronous programming.
The app follows the MVVM + Router architecture with a Linear Tree Style Dependency Management system. The architecture is designed to ensure separation of concerns, testability, and scalability.
-
Builder:
- Takes
Dependencyand builds/returns aRouter. - Responsible for constructing the dependency graph and initializing the flow.
- Takes
-
Router:
- Manages the flow scope, including the
View,ViewModel, and dependency graph. - Ensures that all components are released from memory when the flow is dismissed.
- Handles navigation and flow coordination.
- Manages the flow scope, including the
-
Dependency:
- A tree-structured dependency graph where child components can request properties from their parents.
- Lifecycle is maintained by the
Router. When theRouteris detached, all associated dependencies are deallocated.
-
ViewController:
- Abstracts most of the action handling away from the
View. - Delegates actions to the
ViewModel. Viewcomponents are self-contained and only responsible for layout/content.
- Abstracts most of the action handling away from the
-
ViewModel:
- Acts as a bridge between the
View(ViewController) and the Domain/Business logic. - Holds a private internal mutable state, which is mapped to
ViewStateon each update cycle. - Converts user commands into operations that the Domain layer can understand.
- Injects
UseCasesand delegates navigation to theRouter.
- Acts as a bridge between the
The app is divided into three main layers, each with its own responsibilities and test bundles:
-
Domain Layer:
- Contains pure business logic.
UseCasesencapsulate self-contained business operations.- Has no external dependencies.
-
Data Layer:
- Depends on the Domain layer (Dependency Inversion Principle).
- Handles data operations such as:
- Services (e.g., API calls).
- Repository implementations (
RepoImpls). - Mapping between Data Models and Domain Entities.
-
Presentation Layer:
- Depends on the Domain layer.
- Converts business logic (via
UseCases) into content for theView. - Handles UI updates and user interactions.
-
Single Screen UI:
- Currency selection via picker.
- Amount input for exchange.
- Real-time conversion result display.
-
Polling Mechanism:
- Automatically refreshes exchange rates every 15 seconds when the user is not editing.
-
Reactive Programming:
- Uses Combine to handle state updates and data binding.
-
Modern Swift Concurrency:
- Utilizes
async/awaitfor asynchronous operations.
- Utilizes
The app uses a Linear Tree Style Dependency Management system:
- Each child component can request properties from its parent.
- Dependencies are scoped to the
Routerand released when the flow is dismissed.
Each layer has its own test bundle:
- Domain Layer: Unit tests for business logic and
UseCases. - Data Layer: Unit tests for services, repositories, and data mapping.
- Presentation Layer: Unit tests for
ViewModeland UI logic.
-
Automation:
- Generate the app using tools like Tuist or Xcodegen.
-
CI Automation:
- Set up continuous integration for automated testing.
-
Mock Generation:
- Use tools like Sourcery to automate mock creation for testing.
Currently no 3rd party libraries.
- Autolayout:
- Currently no third party tool is used, would beneficial if Snapkit or EasyPeasy or own layout manager can be added.