(I used up all my free credits)
THIS PROJECT HAS 2 FEATURE LAYERS I VIRTUALLY SEPARATED IN FOLDERS CALLED TRANSACTION AND TRANSACTION DETAILS . THE FEATURE LAYERS CONTAINS THEIR OWN USECASES, API LAYER and Presentation layer . IN THE APP FOLDER IS THE COMPOSITION AND NAVIGATION WHERE I COMPOSE THE OVERALL OBJECT GRAPH AND PASS IT TO THE SCENE DELEGATE TO RUN.
💡 My Motivation for this was based on simulating a real world making sure I think of the design in a critical and scalable way backed by tests which was very fun to do.
- Installation Guide
- Demo Videos
- Requirements
- Tools
- Frameworks
- Concepts
- Architecture
- Testing Strategy
- CI/CD
- Security
Thank you for reading and enjoy! 🚀
- clone the project from the main branch and run the simulator.
- Mock Server completes with success or failure by randomness.
Test that everything is wired up correctly by running tests for CI_IOS
targets to check the communication with both mocked backend and validate that all tests pass.
- ✅ Xcode 15.0
- ✅ swift-driver version: 1.87.1 Apple Swift version 5.9 (swiftlang-5.9.0.128.108 clang-1500.0.40.1)
- ✅ SwiftUI
- ✅ Combine
- ✅ Foundation
- ✅ UIKit
- ✅ MVVM, Clean Architecture
- ✅ Modular Design
- ✅ SOLID Principles
- ✅ TDD, Unit Testing, Integration Testing, Snapshot Testing, and UI Testing using Page Object Pattern
- ✅ Dependency injection and Dependency Inversion
- ✅ Composition Root, Decorator Patterns
- ✅ Domain-Driven Design
Please create a SwiftUI App based on the following User-Stories:
✅ As a user of the App, I want to see a list of (mocked) transactions. Each item in the list displays bookingDate
, partnerDisplayName
, transactionDetail.description
, value.amount
, and value.currency
. (see attached JSON File)
✅ As a user of the App, I want to have the list of transactions sorted by bookingDate
from newest (top) to oldest (bottom).
✅ As a user of the App, I want to get feedback when the loading of the transactions is ongoing or an Error occurs. (Just delay the mocked server response for 1-2 seconds and randomly fail it)
✅ As a user of the App, I want to see an error if the device is offline.
- From the Apple docs:
Always attempt to make a connection. Do not attempt to guess whether network service is available, and do not cache that determination.
It’s common to see iOS codebases using SCNetworkReachability, NWPathMonitor, or third-party reachability frameworks to make decisions about whether they should make a network request or not. Unfortunately, such a process is not reliable and can lead to a bad customer experience.
hence i did not use SCNetworkReachability or NWPathMonitor and just have a generic Connection Error
in case my request fails
✅ As a user of the App, I want to filter the list of transactions by category
.
✅ As a user of the App, I want to see the sum of filtered transactions somewhere on the Transaction-list view. (Sum of value.amount
)
✅ As a user of the App, I want to select a transaction and navigate to its details. The details view should just display partnerDisplayName
and transactionDetail.description
.
✅ As a user of the App, I like to see nice UI in general. However, for this coding challenge fancy UI is not required.
features can be vertically sliced and composed in the main module by following dependency injection/inversion, clean architecture, and domain driven design which allows us to have decoupled modules and gives you the freedom to compose however you like. Makes the code more testable maintainable and soft making it easy to add new requirements. I added a general example of a design diagram below
- I didn't start with swiftui but rather with scene delegate so I could try to mimic Simulation cause i know a lot of legacy code is in uikit and the transition to swiftUI would involve a lot of hosting view controllers.
- navigation is a known issue in swiftui and decoupling it from the views is not ideal maybe with navigation stack it is but with the constrain of ios 16 so i prefer to use at the moment flows or coordinator with hosting view controllers to decouple the view from navigation .
- I tried to compose swiftui views using multiple child views so we can easily reuse a view anywhere in the codebase if needed
- i stuck to @state and @stateobjects and not the new @observable macro again due to ios version constraints
We use Reactive programming and are currently moving from a Rx-Swift to Combine. For asynchronous code we are moving to Swift Concurrency.
- again keeping in mind the Simulation. I started out with closure for asynchronous code like networking backed by tests and then i provided the clients the option to use async await using the checkedthrowing api provided by apple . the goal is to let the clients use the new async await while slowly moving away from closure based syntax or delegate if you use that but i guess completion handlers are more common. its important to test your async concurrent code and this involves a difference here as with completion handlers it's very easy to use a spy and capture it and complete whenever and however you want but we don't have that luxury with async await so i moved from spying to stubbing .
- i limited the use of combine to the presentation layer only for my view models.
- ideally combine or any 3rd party frameworks shouldn't be coupled with your layers especially your domain layer . your domain layer shouldn't depend on anything to allow for decoupled code
- if you want to decouple one layer more you can add in an adapter layer which would adapt bw your domain and presentation layer and this way your presentation and domain don't know about each other. The adapter lives in the composition root so it knows about both the layers . you can also use universal abstractions to move away from dependency injection and just compose .
We try to keep to as few external dependencies as possible. However, we use Swift Package Manager when we need to add a dependency.
keeping this point in mind i used spm only snapshot my views for regression using SnapshotTesting library and nothing else
i tried to use github actions to achive some sort CI