-
Notifications
You must be signed in to change notification settings - Fork 3
Version 2 Roadmap
As a concept Composed is great, but it falls down when not used in the ideal way. This is particularly true for consumers that update frequently (e.g. via a web socket providing frequent data updates) or do not use Core Data and so have to perform diffing manually. The main issues ran in to are:
- Crashes when performing many updates at once, often caused by the collection view's internal state differing from Composed's state
- Inflexibility of API (particularly delegates)
- Lack of understanding of pitfalls that can hurt performance
Changing the mappings to be per-section rather than at the top level was a good move but it was not enough to avoid crashes and performance hits when the UI was added
I will outline what I think a roadmap for a v2 release could be. Hopefully we can comment on wiki pages. If not this will be moved to an issue. I'd like to use GitHub Discussions but the beta appears to require 3 contributors. I went for a wiki because then we can both edit it (unlike an issue message).
Delegate calls "up" the chain (e.g. eventually to UICollectionView
) look a little complex. For example in SectionProviderUpdateDelegate
the Section
that was inserted/deleted it passed up. This appears to be mainly for setting the updateDelegate
to be the SectionProviderMapping
. As outlined below I think the SectionProviderMapping
could be removed, allowing the section's delegate to be the SectionProvider
and simplifying the delegate.
Some of the recent improvements to ComposedSectionProvider
might allow for the removal of SectionProviderMapping
. When originally proposing the architecture of each layer performing mappings I did not envision the top level SectionProviderMapping
being necessary. Having a single type could've meant that the bugs in ComposedSectionProvider
would've been caught earlier since SectionProviderMapping
already has tests (which ComposedSectionProvider
now has anyway).
There may be some extra use of SectionProviderMapping
that I'm not seeing but to me it currently looks superfluous.
invalidateAll
is used in a few places in the core of Composed but poses a huge performance hit when used frequently. This is mainly because it will invalidate the entire tree of sections, despite only a single section being invalidate.
One solution could be to remove it and perform a simple remove all then add all in the sections where it's currently used. At least this should improve the performance hit.
Another solution could be to update all of Composed to not use invalidateAll
and heavily warn against its use in the docs. For example I was hit by ArraySection.replaceSubrange(_:with:)
using invalidateAll
, which I thought would be quite efficient!
Ideally this would be entirely removed once diffing is introduced.
The main thing I think about regarding this is the need for CollectionSelectionHandler.sizingStrategy(at:metrics:environment:)
to be cached by the implementer. I understand why this is the case but it would be nicer to have these requirements outlined somewhere if the API can't be improved to accommodate them.
The easiest way to crash now is to perform multiple delegate calls in quick succession. Mainly because the UI (e.g. UICollectionView
) has its own internal state vs what Composed has. These issues are outlined in https://github.com/composed-swift/ComposedUI/issues/13 and https://github.com/composed-swift/ComposedUI/issues/8.
I'm unsure what the best fix for this would be. I changed the internals in https://github.com/composed-swift/Composed/pull/14 but this makes implementing sections a huge pain and could have real issues when consumers try to implement their own sections.
Currently calling willBeginUpdating
sets a flag to "pause" updates and then didEndUpdating
will trigger the updates. But calling willBeginUpdating
multiple times does not create a need to call didEndUpdating
multiple times. This often requires consumers to create their own sections that mimic ArraySection
just to have all updates performed in a single update. Making these calls balanced could improve this situation.
In the same way performBatchUpdates
of UICollectionView
asks you to pass a closure that they'll call, we could do the same. The main advantage to this is that the data updates would be synchronised with the UI. https://github.com/composed-swift/Composed/pull/14 is kind of like this, except the sections are keeping track of pending vs applied data, which as mentioned is not the best for an implementation point of view.
This for me is the hardest problem to solve.
Arguably the best feature of Composed is the separation of the sections, but it's quite tricky to mix types within a section. Mixing types can be useful for certain kinds of layouts, e.g. to get sticky headers for free all cells must be in the same section, but even a somewhat simple data structure can prevent this.
Providing a Section
that composes 2 or more sections and performs the mapping of indexes, delegate calls etc. would allow for this kind of UI to be created while maintaining the separation of sections. It could be very similar ComposedSectionProvider
but conforming to Section
rather than SectionProvider
.
I don't think this is possible now because the section itself is passed up the delegate chain and either a ===
is used or something else gets confused. I've tried it and got strange crashes so gave up. With the above delegate changes I would hope this would be possible.
This could be part of where the function builder API is added.
I would summarise the tasks to be:
v2.0
- Copy over
Section
,SectionProvider
,AggregateSectionProvider
,ArraySection
,ComposedSectionProvider
, andSegmentedSectionProvider
. - Copy
SectionUpdateDelegate
andSectionProviderUpdateDelegate
. But for now leave outwillBeginUpdating
,didEndUpdating
, andinvalidateAll
. Removesections: [Section]
fromSectionProviderUpdateDelegate
functions. - Add tests for copied over types
- Add delegate methods for updates
- Add
UICollectionView
support
v2.1
- Add some form of diffing support (even if only in
ArraySection
) - Support "data mixing" e.g.
ComposedSection