0% found this document useful (0 votes)
88 views7 pages

Code Reviews

This document provides guidelines for reviewing code on iOS projects at One App. It discusses philosophies like focusing on the code's story and not criticizing developers. It also provides specifics on reviewing code structure, dependencies, scenes, view controllers, view models and use cases for both MVVM and older architectures. Reviewers are advised to leave constructive comments, check for common issues, and make sure code follows architecture guides.

Uploaded by

Jose Hidalgo
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
88 views7 pages

Code Reviews

This document provides guidelines for reviewing code on iOS projects at One App. It discusses philosophies like focusing on the code's story and not criticizing developers. It also provides specifics on reviewing code structure, dependencies, scenes, view controllers, view models and use cases for both MVVM and older architectures. Reviewers are advised to leave constructive comments, check for common issues, and make sure code follows architecture guides.

Uploaded by

Jose Hidalgo
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 7

iOS - Code Reviews

This article documents how to review code in One App iOS. Helpful for new and remote employees to get and stay aligned.

Philosophy
The code tells a story
Don’t make the reviewee cry
Value others time
Language: You review the code not the developer
Don’t put comments on the general PR if possible
Performing the code review
How to Review
General code smells
Guide (MVVM Architecture)
General module folders structure
Module dependencies
Scenes
Scene coordinator
Scene ViewController
Scene viewModel
Use cases
Guide (old architecture)
In general
Coordinators
Presenters
Other things to check in presenters
Use cases

Philosophy
Why are code reviews important?

Save your time - re-use coworker’s knowledge


Teach you new stuff - Learn from your coworkers
Show off your work - Share your knowledge with the team

The code tells a story

When you review a piece of code you should understand the functionality just reading it. If don’t, you usually have two ways of thinking about it:

I’m stupid, so probably this is ok, I will approve it. (that impostor syndrome again)
I don’t understand this code and it should be understood by a junior developer. It can be improved.

The second one must be the attitude to follow. Complex abstractions, overengineering, general and meaningless names, are a self-sabotage for
you and your workmates in the future.

Don’t make the reviewee cry

Reviewing is helping others, some times junior developers. Talk offline if it is necessary. Sometimes the problem is bad communication, poor
requirements or the scope of the problem.

If there is official documentation in Confluence about the review comment, add the link, this helps spreading the official conventions and good
practices.

Value others time

Most developers don’t review their own code, but coworkers aren’t your personal QA. You are responsible for your code quality. Review yourself
with the same criteria that other’s code.

Language: You review the code not the developer

Review the code, not the person. This must be considered in the language used. Don’t say “this is wrong”. Ask questions. Avoid negative
comments.
Examples:

You shoudn’t do it like this. Use optional

Bad review comment


This code would be better with optional. Here’s how to do it:...

Good review comment

Don’t put comments on the general PR if possible

Put comments in the diff in the appropiate point, not in the whole diff. When the comment is solved, be kind and write down an outcome (if you
have time). The comments are the history of its resolution and can be valuable in the future.

Performing the code review

How to Review

Make two passes over the PR if it's substantial.


On the first pass, come to an understanding of the code change at a high level, the modules affected paying attention to changes that can
affect several modules or other countries.
On the second pass, pay more attention to semantic details.
Review the main points listed in the Guide when it applies.
Check the structure when it is a new module

General code smells


A scene presentation module must not depend on another presentation module. the dependencies in a scene must be only vertical. For
example, the cards module must not depend on the login module.
Direct references to local country functionalities are not allowed. For example, add a library only for a country local functionality, adding
keys or naming related to a concrete One App vertical… in this cases core must offer a way of inject local country functionality.
Architecture mixtures. The conventions are clear about the separation of MVP and MVVM. Check the iOS - MVVM & MVP interoperability
articles to understand how both archs can work together.
A change in a public core API protocol. If you review a change in an existing Core public API protocol, this could break an app. Double
check that these kind of changes are protected with a default implementation, value or optionals, for example.

Guide (MVVM Architecture)

General module folders structure

In general it should use a template for the new module. There should not be a folder “standard” because it is from and old separation between
“standadard” and “legacy” modules that was deprecated.

It is correct to have an “MVVM” folder to group the module content.

Module dependencies

There will be a module/dependencies folder with two resolver files. One for local dependencies and one for external dependencies. See iOS -
Dependency injection (DI)

Things to check:

The folder exists


Exist a general dependency resolver file ModuleNameDependenciesResolver
ModuleNameDependenciesResolver protocol has a variable referencing the ExternalDependenciesResolver

var external: FundsHomeExternalDependenciesResolver { get }


Scenes

The module has scenes (see iOS - Scenes ) there is an standard structure that must be followed.

Scene coordinator

See the coordinator types in the architecture in the app navigation context, and general coordinator info

Things to check

A public protocol with the coordinator interface inheriting from BindableCoordinator


A default coordinator implementation. Naming starting with Default… and implementing the protocol

Usually we will have the following common implementation with an init and the lazy dependencies.

final class DefaultCoordinatorName:


CoordinatorProtocolName {

var onFinish: (() -> Void)?


weak var navigationController: UINavigationController?
var childCoordinators: [Coordinator] = []
private let externalDependencies:
SavingDetailExternalDependenciesResolver
lazy var dataBinding: DataBinding = dependencies.resolve()

private lazy var dependencies: Dependency = {


Dependency(dependencies: externalDependencies, coordinator:
self)
}()

public init(dependencies: ModuleDependenciesResolver,


navigationController: UINavigationController?) {
self.navigationController = navigationController
self.externalDependencies = dependencies
}
...

Scene ViewController

The view controller should be correctly related to the view model and the dependencies resolver.

Things to check:

private reference to the viewmodel protocol


i.e. private let viewModel: SavingDetailViewModelProtocol
private ref to dependencies resolver
i.e. private let dependencies: SavingDetailDependenciesResolver
A reference for the view model reactive subscriptions
private var anySubscriptions: Set<AnyCancellable> = []
an init
init(dependencies: SavingDetailDependenciesResolver) {
self.dependencies = dependencies
self.viewModel = dependencies.resolve()
super.init(nibName: nil, bundle: .module)
}

Scene viewModel

The view model uses a state mechanism (see iOS - ViewModel ) and at least a basic state enum and observable must be offered.

Things to check:

A state enum

Use cases

The use cases in MVVM architecture are described here in confluence.

Check list

Don’t import UIKit, UI or in general any device dependant API. Use cases should be only bussiness logic
Use cases returns a publisher with a representable. Never a DTO. (see iOS - Data Objects )

Guide (old architecture)

In general

In PLUI, adding the new controls to example app


Check that only custom views are added in PLUI. Complete reusable scenes must be added to PLScenes
Check the access level for classes, methods and properties.
Look for “import Commons”
Commons was moved to CoreFoundationLib in Feb 2022
Check // TODO: lines. We must avoid them. It generates warnings

Coordinators

A protocol to define the interface and its implementation in a extension

protocol PLBeforeLoginCoordinatorProtocol { }
...
extension PLBeforeLoginCoordinator : PLBeforeLoginCoordinatorProtocol {
...
}

A coordinator implementation of ModuleCoordinator. Note final modifier


final class PLBeforeLoginCoordinator: ModuleCoordinator {
weak var navigationController: UINavigationController?
private let dependenciesEngine: DependenciesResolver &
DependenciesInjector

init(dependenciesResolver: DependenciesResolver,
navigationController: UINavigationController?) {
self.navigationController = navigationController
self.dependenciesEngine = DependenciesDefault(father:
dependenciesResolver)
self.setupDependencies()
}

func start() {
...
}
}

Note

dependenciesEngine property is usually private. It make no sense a internal because it is the default access level.
A section with dependencies registration in a private extension

private extension PLBeforeLoginCoordinator {


func setupDependencies() {
...
}
}

Presenters

A presenter protocol to offer an interface and its implementation

protocol PLBeforeLoginPresenterProtocol: MenuTextWrapperProtocol {


...
}

...

extension PLBeforeLoginPresenter: PLBeforeLoginPresenterProtocol {


...
}

Usually with life cycle methods like viewDidLoad() and more specific methods.
The presenter implementation

final class PLBeforeLoginPresenter {


weak var view: PLBeforeLoginViewControllerProtocol?

init(dependenciesResolver: DependenciesResolver) {
self.dependenciesResolver = dependenciesResolver
}
}

Note:

final modifier
the dependencies resolver is inyected
the presenter has a weak reference to the scene view.
A private extension with more implementation

private extension PLBeforeLoginPresenter {


...
}

Other things to check in presenters

Use cases are launched from presenters using scenarios.

Use cases

The use case definition

final class PLGetPublicKeyUseCase: UseCase<Void,


PLGetPublicKeyUseCaseOkOutput, PLUseCaseErrorOutput<LoginErrorType>>,
PLLoginUseCaseErrorHandlerProtocol {

Note:

is marked final
Naming, ending in UseCase
The recommendation is defining a struct for Input and Output
Include dependenciesResolver in the init method
...
var dependenciesResolver: DependenciesResolver

public init(dependenciesResolver: DependenciesResolver) {


self.dependenciesResolver = dependenciesResolver
}
...

Implementation of executeUseCase method

public override func executeUseCase(requestValues: Void) throws ->


UseCaseResponse<PLGetPublicKeyUseCaseOkOutput,
PLUseCaseErrorOutput<LoginErrorType>> {
...
}

Other considerations

¿Is acceptable not using concrete input and output structs?


It depends, in some cases can be stupid using a struct to return a simple value.
It is an error returning a type defined in the data library.

You might also like