- Architecture
- Concurrent code
- Dependency Injection
- Code generation
- Tests
- CI/CD
- Tools and dependencies
This application follows the Clean Architecture principles.
Check out more: https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html
The presentation layer of this application is built entirely using SwiftUI in combination with the MVVM+C (Model-View-ViewModel-Coordinator) pattern.
The navigation in this app is powered by SwiftUI's NavigationView with NavigationPath.
Alamofirelibrary is used to perform HTTPS requests.SwiftDataframework is used to store data.CoreLocationframework is used to retrieve the user's location.
The concurrent code in this project is implemented using the Swift Concurrency library. Strict Concurrency Checking setting is set to Complete.
All cases which cannot be written using the Concurrency library, are achieved by Combine library.
Dependency Injection is implemented using protocol, property wrapper, subscript, KeyPath, and Mirror. KeyPath provide a major advantage by ensuring type safety at compile time. The Root type of a KeyPath is defined to require an object conforming to AllDependencies typealias, which aggregates all dependency protocols. Swift subscript enable easy access to specific object via its KeyPath, while @Inject property wrapper simplify implementation and reduce boilerplate code. This way
For the application code, Mirror reflects the DependenciesContainer and returns an object's instance matching the provided KeyPath's type and name. For testing, dependencies are stored in a dictionary: [PartialKeyPath<AllDependencies>: Any], making it easier to inject mock dependencies.
This way it's not possible to access to an object that has not been declared in the DependenciesContainer and this reduces number of app failures to a minimum.
SwiftGenis used to generate resources such as images, colors, strings.SwiftLintis used to lint the code.Xcodegenis used to generate project (project.pbxproj) based on YAML files.
This application utilizes the XCTest framework for testing purposes.
All objects from the Data Source, Application, and ViewModels from the presentation layer are fully tested.
The application is informed about the type of tests via ProcessInfo launchArguments, which are defined in the project Scheme.
- Tests are structured following the
Given-When-Thenconvention, ensuring clear and understandable test cases for various scenarios. - Assertions in tests are handled by
XCTAssert()functions. - Asynchronus tests are achived by
XCTestExpectationclasses.
To mock a network connections during UI tests, a local WireMock server is used.
The WireMock server is started before the UI tests begin and it is shut down after the tests are completed using scheme's pre-action and post-action.
The server’s response data and behavior are defined in the __files and mappings in the WireMock directory.
Why so complcated 😀?
Setting up a local server basically does not require any changes to the application source code, so it works almost the same way as the release version of the application. The defined server responses are in the same form as the responses of the real server. This ensures that the tests are able to check the correctness of decoding, mapping data, UI and etc. The application runs and behaves during UI tests exactly as it would in a real-world scenario, providing reliable and comprehensive test results.
For quick and efficient validation of data display correctness, snapshot tests are performed.
During the tests, the current state of the application’s UI is captured using: XCUIScreen.main.screenshot().
Then the captured screenshot is compared to baseline images stored in the __Snapshots__ directory, located in the UI test directory.
Discrepancies between the current and baseline images indicate visual regressions.
To achieve maximum precision of the compared images (95%), the simulator selected to handle them has the status bar and appearance parameters changed before the UI tests.
During tests conducted on a virtual machine, there is no possibility to view simulator screenshots. In order to be able to view what gone wrong xcresult files of UI tests are keep as a Github Action artifact and is available for download after the pipeline is completed. It is very handy to use xcresult directly beacose this files contain timelapse of whole tests.
To automate the Code Review process, the Danger tool is used. The Xcode-Summury plugin allows you to display test results, errors, and warnings in the code generated by Xcode and SwiftLint in Pull-request comments.
Xcode-Summury is based on the xcresult file. The project contains a separate scheme for UI tests and unit tests. In order not to have another, common scheme, both test schemes build the project in the same configuration, then the tests are execute. A separate xcresult file is generated for each test. After all, both files are merged using the xcresulttool tool.
- Danger: https://github.com/danger/danger
- Danger Xcode Summary: https://github.com/diogot/danger-xcode_summary
- SwiftLint: https://github.com/realm/SwiftLint
- SwiftGen: https://github.com/SwiftGen/SwiftGen
- XcodeGen: https://github.com/yonaskolb/XcodeGen
- SwiftSnapshotTesting: https://github.com/pointfreeco/swift-snapshot-testing
- WireMock: https://github.com/wiremock/wiremock
- Alamofire: https://github.com/Alamofire/Alamofire







