-
Notifications
You must be signed in to change notification settings - Fork 23
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鈥檒l occasionally send you account related emails.
Already on GitHub? Sign in to your account
The great flattening. [[Model]] -> [Model] #48
Conversation
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.
Awesome work - thanks for digging into this!
I'm definitely onboard with Option A. Something like what you mentioned:
public protocol SectionedModelCollection {
var sectionState: [ModelCollectionState] { get }
}
To get the "free" section support on ModelCollections, we could flip conformance like this:
extension SectionedModelCollection where Self : ModelCollection {
public var sectionState: [ModelCollectionState] {
return [state.models]
}
}
which would require classes opting in to section support (which is probably fine). And then something like this could make code that requires sections (e.g. diff engine) easier, or "free":
extension ModelCollection {
public func withSections() -> SectionedModelCollection {
if let sectionedModel = self as? SectionedModelCollection {
return sectionedModel
}
return StaticSectionedModelCollection(self)
}
}
Long way of saying, this is great. Let's run with it!
/// Returns a dictionary mapping `ModelId`s to their index path within the given `sections` object. | ||
public func generateModelIdToIndexPathMapForSections(_ sections: [[Model]]) -> [ModelId: ModelPath] { | ||
/// Returns a dictionary mapping `ModelId`s to their index path within the given `models` object. | ||
public func generateModelIdToIndexPathMapForSections(_ models: [Model]) -> [ModelId: ModelPath] { |
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.
We could remove this in the name of a smaller API surface (and it's not hard for apps to write). Also, we don't use it anymore.
|
||
/// Returns a dictionary mapping item `ModelId`s to their index path within the target `ModelCollection`. | ||
public var modelIdToIndexPathMap: [ModelId: ModelPath] { | ||
return generateModelIdToIndexPathMapForSections(sections) | ||
return generateModelIdToIndexPathMapForSections(models) |
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. Could remove modelIdToIndexPathMap
} | ||
|
||
/// Convenience methods to return the total number of items in a `ModelCollection`. | ||
/// TODO:(danielh) deprecate? |
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'm a fan of removing this now, while we're making breaking changes.
} | ||
} | ||
return true | ||
return models.isEmpty |
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.
we also have ModelCollectionState.isEmpty
.. wonder if that's too redundant.
return typed | ||
} | ||
} | ||
guard indexPath.modelSection == 0 else { return nil } // TODO:(danielh) throw/log error? |
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
a) moving these index path related methods to SectionedModelCollection
?
b) moving the three indexpath methods to a protocol and sectioned model collection & model collection can have their own implementations?
// swiftlint:enable nesting | ||
|
||
let initialReducedStates = CollectionStateReduction() | ||
let reducedStates = substates.reduce(initialReducedStates) { prevCollectionStateCounts, state in |
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.
until swift 4 reduce
improvements land, should this be a for substate in substates
?
|
||
extension ComposedModelCollection { | ||
|
||
public static func multiplexing(_ modelCollections: [ModelCollection]) -> ComposedModelCollection { |
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.
馃憤
Core/Source/MVVM/DiffEngine.swift
Outdated
// The diff algorithm does not move sections around at this time. If sections had IDs, | ||
// moving sections would be possible. | ||
// The diff algorithm does not move models around at this time. If models had IDs, | ||
// moving models would be possible. |
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 comment should still refer to sections
Core/Source/MVVM/DiffEngine.swift
Outdated
// First, make sure there are the right number of sections. This code always adds or removes sections | ||
// from the end. In the future, if sections themselves had IDs, it would be possible to support | ||
// First, make sure there are the right number of models. This code always adds or removes models | ||
// from the end. In the future, if models themselves had IDs, it would be possible to support |
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, probably still sections
instead of models
since the diff engine still works on sections
Core/Source/MVVM/DiffEngine.swift
Outdated
// Since sections don't have IDs, this code keeps the same section cursor on the left and right. | ||
// If sections had IDs this code could be smarter. | ||
// Since models don't have IDs, this code keeps the same section cursor on the left and right. | ||
// If models had IDs this code could be smarter. |
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
Two more thoughts:
|
This reverts commit 3b2646e.
Split out sectioning behavior from `ModelCollection` into `SectionedModelCollection` to simplify all the model collection code that only needs to support a single section.
Overview
Took a crack at #theflattening on my 馃洬 home. This is an initial cut of transitioning to where the most common implementations of
ModelCollection
don't have to operate on sectioned data but can instead store and operate on a simple array ([Model]
)This is a first draft, wanted to push up a bit early to get some feedback on direction. Some known remaining TODOs before merge:
Approach to transition
Decided to keep the diff engine sectioned, this meant
ModelCollection
(or the datasource?) needed to still provide some concept of sections so the previous property onModelCollection
sections: [[Model]]
remains where the default implementation is just a wrapper on the newmodels: [Model]
property.Alternatives Considered
Option A: Protocol + Default Conformance
Add a new protocol
SectionedDataSource
which could look something like:and then default conformance for a
ModelCollection
could be as simple as:This would be ideal since you could have the common single-sectioned model collections be completely obvlivious to the nature of sections while still overriding that in order to support them in cases where its valuable (ex: a
MultiplexedModelCollection
). Unfortunately swift doesn't allow adding conformance to a protocol via an extension. Which removes a lot of the awesomeness of this solution.Option B: Sectioned as root protocol/interface
Make ModelCollection inherit directly from
SectionedModelCollection
:This has some nice properties, in that it allows all existing model collections to remain mostly unchanged, while reaping the benefits of being single-sectioned for the common code paths. However it has a few downsides:
CollectionViewModelDataSource
/ViewController
Option C: Remove concept of sections from Pilot
If we remove sections from the diff engine, it becomes pretty easy to flatten all references to
[[Model]]
in the app and delete a bunch of code. However, this comes at a pretty significant tradeoff for future use-cases where using sections makes a lot of sense, and didn't seem worth biting off at this moment.