badoo / Chatto

A lightweight framework to build chat applications, made in Swift
MIT License
4.49k stars 595 forks source link

Composable messages #764

Closed wiruzx closed 2 years ago

wiruzx commented 2 years ago

Composable Message View Architecture

In this pull request I introduced a new way to setup chat messages ui.

We're going to make this a new default, but going to save compatibility with the old approach as well.

Please keep in mind, that the work on this feature is not done, and will be continued as following milestone: Composable Message Architecture

You can get the highest-level overview to this pr by looking at ChatItemPresenter's implementation.

It has a few dependencies, responsible for all of the work:

Binder

Establishes connection between View and ViewModel.

LayoutAssembler & ViewAssembler

Assembles View Hierarchy from the list of factories & given hierarchy. We create the same hierarchy for LayoutProviders.

FactoryAggregate

Factory, to which we register all the factories required to create some part of the message. Currently those are View Factory, ViewModel Factory and LayoutProvider Factory.

Why do we need those?

  1. Because of the cell reuse we need to have a way to create View and ViewModel Separately. The View is going to be reused between messages with the same View structure, while ViewModels are kept in memory per each loaded message.

  2. In order to make composition of reused views possible, we also needed to separate the view hierarchy. We're doing this right now by specifying parent and one or more children to View/Layout assebler:

    assembler.register(child: childKey, parent: parentKey)
    // or
    assembler.register(children: [.init(firstChild), .init(secondChild)], parent: parentKey)

    Currently we need to do it twice for View and for Layout. This is going to be fixed in #743

Type Safety

Views, ViewModels and Layout for the Views are created in different factories and at different times. So in order to check that View is compatible with given ViewModel and given Layout could be applied to the View, I introduced a BindingKey which will help to make sure that types are matching.

When you register View, ViewModel and LayoutProvider factoreis into the FactoryAggregate, it checks that those 3 entities are compatible with each other. It also returns a binding key with type information: BindingKey<View, ViewModel, LayoutProvider>, so it can be used in other functions where we need the type information.