Skip to content

Commit

Permalink
Implement Clean Architecture
Browse files Browse the repository at this point in the history
An example implementation of the Clean Architecture, modeled after the one in the Android template repo. It consists of a 'home' view with back-end services that fetch a dummy 'user'.
  • Loading branch information
mbernson committed May 10, 2024
1 parent 3d86317 commit 4581e65
Show file tree
Hide file tree
Showing 26 changed files with 841 additions and 40 deletions.
87 changes: 84 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@

This is a template for creating iOS projects at Q42. It has opinionated defaults and boilerplate, based on how we do iOS projects at Q42.

## How to use it
## Using this template

1. In GitHub, press "use this template" to create a new repository.
2. Rename your project using the included Python script.
1. Click the green "Use this template" button in the Github repo to create your own repository with
this code template.
1. Clone the new repo locally on your machine.
1. Run `python ./scripts/rename-project.py` or `python3 ./scripts/rename-project.py` from the
project root, to change the project name. The script will ask for your new project name and update all references.

## Features

Expand All @@ -24,6 +27,84 @@ The Xcode project is configured to use 4 spaces for indentation.
For linting Swift source code, we use [SwiftLint](https://github.com/realm/SwiftLint).
A configuration for [SwiftFormat](http://github.com/nicklockwood/SwiftFormat) is also included.

## App architecture

### Core principles

This app is built using SwiftUI and targets iOS 15 and higher. We use SwiftUI as much as possible, but fall back to UIKit views using view(controller) representables where needed.

We try to stick to the Apple conventions and write idiomatic SwiftUI code. Do things the Apple way. Lean in to the platform instead of fighting it.

Keep it simple. Less is more.

### Architecture patterns

We use the [Clean Architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html) pattern, combined with dependency injection.

#### Clean Architecture layers

- _UI_ with SwiftUI
- _Presentation_ with the ViewModel
- _Domain_ for domain models, UseCases and other domain logic.
- _Data_ for data storage and retrieval.

#### Use cases

- Use cases are single-purpose: GetUserUseCase, but also: GetUserWithArticlesUseCase.
- Use cases can call other use-cases.
- Use cases do not have state, state preferably lives in the data layer.

#### Dependency injection

Dependency injection (DI) means that objects don't instantiate the objects or configurations that they require themselves, but they are passed in from the outside.
This is useful, because it makes code easier to test and easier to change. It promotes good separation of concerns.

We use [Factory](https://github.com/hmlongco/Factory) as a DI container.

### Modules & libraries

* Preferably use the Swift Package Manager for dependencies. Use other package managers only if there's no other option.
* Only extract code into a package if there are strong reasons to do so. For example:
* It is used from at least two different targets/packages, or is a candidate to be extracted to an open-source package.
* It is completely self-contained.

When choosing a third-party library, prefer libraries that:

* Are written in idiomatic Swift or Objective-C that sticks to best practices.
* Have as few dependencies of its own as possible. Preferably none.
* Aren't too big, in order to keep compile times and bloat in check.

### Testing

* For business logic, we write unit tests.
* For testing the user interface, we write UI tests in a behaviour-driven way using the [Salad](https://github.com/Q42/Salad) library.
* Tests are run on CI (GitHub Actions). Tests must pass before a PR may be merged and before any sort of build is created.

### Views

* Keep views small and focused. When a view becomes large, split it up into smaller views.
* Every view gets a UI preview if at all possible. The preview should show the view in different states using dummy data.
* We use [custom SF Symbols](https://developer.apple.com/documentation/uikit/uiimage/creating_custom_symbol_images_for_your_app/) whenever a custom icon is needed, so that they render in a consistent manner.

### Accessibility

* Every new component or control should be audited for basic accessibility support:
* Dynamic type size support
* VoiceOver support
* Also consider:
* Bold text support
* High contrast support
* Use `accessibilityRepresentation` on custom controls to make them accessible.

### Localization

String catalogs are used to localize the project. The default languages supported are English and Dutch.

### Async code

* Asynchronous code should be written using async/await whenever possible.
* [Combine](https://developer.apple.com/documentation/combine) should only be used when async/await or `AsyncSequence` fall short, and more complexity is needed to solve the problem at hand.

## Continuous integration

GitHub Actions is used for continuous integration (CI). The CI runs the automated tests when you make a pull request.
Expand Down
Loading

0 comments on commit 4581e65

Please sign in to comment.