-
Notifications
You must be signed in to change notification settings - Fork 593
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Composable messages #764
Composable messages #764
Conversation
DummyChatItemPresenter() | ||
} | ||
|
||
public func configure(withCollectionView collectionView: UICollectionView) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think about keeping consistency with the above methods and rename this method to configure(with collectionView: UICollectionView)
?
(Or change the other one. This also applies to the above method)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one is part of public protocol, so not sure if it's a great idea to do renaming here.
I could rename the other one(configure(with collectionView: UICollectionView)
), to match this function (configure(withCollectionView collectionView: UICollectionView)
), but I'm going to remove the protocol completely: #761
} | ||
|
||
public static func registerCells(for collectionView: UICollectionView, with reuseID: String) { | ||
collectionView.register(Cell.self, forCellWithReuseIdentifier: reuseID) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this reuseIdentifier
injected in the init?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is the case, we should probably use the method above, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function has to be a static function, that's why we're passing reuseID
from the outside.
I'm going to fix this in #761
} | ||
|
||
// TODO: Unify viewModels instantiation. We need to create everything at once #743 | ||
private func makeViewModels() -> [Key: Any] { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do u think about using a lazy var instead? It would be a single line
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've already created an issue almost exactly for that: #743
What do you think if I fix it there?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds great ;)
return viewModels | ||
} | ||
|
||
private func makeLayoutProviders() -> LayoutProviders { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above
return layoutProviders | ||
} | ||
|
||
private func makeRootViewSizeProvider() -> AnySizeProvider { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above
) | ||
} | ||
|
||
public let presenterType: ChatItemPresenterProtocol.Type = ChatItemPresenter<ChatItemType>.self |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Niiiice :)
// MARK: - Multiple | ||
|
||
public protocol MultipleContainerViewProtocol { | ||
func addChildren(children: [UIView]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do u think about removing Children
from method signature and only keep it in the parameter?
} | ||
} | ||
|
||
extension CachingLayoutProvder: AnyCachedLayoutProvider { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo? CachingLayoutProvider
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
} | ||
|
||
let fallbackItemPresenterFactory: ChatItemPresenterFactoryProtocol | ||
if shouldUseNewMessageArchitecture { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we group these two if statements?
binder.registerBinding(for: first) | ||
binder.registerBinding(for: second) | ||
|
||
var assembler = ViewAssembler(root: bubble) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will the view assembler make the same registrations as the layout assembler. Can we avoid this duplication somehow?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Going to tackle that one in #749
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?
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.
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:
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.