Naming style
Last modified on Tue 07 Jan 2020

Observables, drivers and the rest of the observable types

In most instances we want to ditch observable from our object names and treat these in the following way:

Not Preferred:

let buttonTapObservable: Observable<Void>
let itemsObservable: Observable<Void>

Preferred:

let buttonTap: Observable<Void>
let items: Observable<Void>

Relays and subjects

An exception to this are Relays and Subjects. Since they aren't only meant to be read and can accept values, keeping Relay and Subject in the name helps to immediatelly recognize their context.

let modelUpdateSubject = BehaviorSubject<Model?>(value: nil)
let formEditsRelay = BehaviorRelay<Model>(value: model)

Actions grouping

Actions which can be grouped should be organized as such. For instance, we've mentioned events and actions that are sent between our module components. In instances where we can do that, a simple struct wrapper works wonders to help you out in organizing the code, if ever so slightly.

An example of that would look like the following:

struct Actions {
    save: Observable<Void>,
    cancel: Observable<Void>,
    ...
}
struct Events {
    ...
}

We declare these in our namespace struct in order to not pollute the rest of the project. Afterwards, simply use them in our default I/O approach during the initial binding:

struct ViewOutput {
    let actions: Actions
}
struct ViewInput {
    let events: Events
    let items: Observable<Item>
}

Note: In cases where it makes no sense to group actions, e.g., let items: Observable<Item> in the above example, it is only important to follow the very first part of the naming section. That includes deciding on a property name, while omitting the observable type from the name.

Presenters' bind function

We should strive towards the exact same name for the bind function, along with its parameters. Please use the following in your projects:

presenter.configure(with output: Module.ViewOutput) -> Module.ViewInput

Handle functions

Considering that all of the actions we need to handle will end up in the given configure function, we'll need to format the code appropriately so that we don't end up with a "Massive View Controller" configure function. The general template we follow is to use handle functions, which will transform our data and then either subscribe or return the value. Example:

func configure(with output: Module.ViewOutput) -> Module.ViewInput {

    _handle(action: output.action)

    let result = _handle(action: output.action)

    return Module.ViewInput(
        result: result
    )
}

Depending on the action name and whether it conflicts with an already given name, for instance _handle(viewDidLoad: output.viewDidLoad), we can make use of labels to further identify what are we going to do in the handle function. In those instances, we would use the viewActionWith label.