Dwifft is a small Swift library that tells you what the "diff" is between two collections, namely, the series of "edit operations" required to turn one into the other. It also comes with UIKit bindings, to automatically, animatedly keep a UITableView/UICollectionView in sync with a piece of data by making the necessary row/section insertion/deletion calls for you as the data changes.
Dwifft is a Swift library that does two things. The first thing sounds interesting but perhaps only abstractly useful, and the other thing is a very concretely useful thing based off the first thing.
The first thing (found in Dwifft.swift
) is an algorithm that calculates the diff between two collections using the Longest Common Subsequence method. If this kind of thing is interesting to you, there's a pretty great paper on diffing algorithms: http://www.xmailserver.org/diff2.pdf
The second thing (found in Dwifft+UIKit.swift
) is a series of diff calculators for UITableView
s and UICollectionView
s. Let's say you have a UITableView
that's backed by a simple array of values (like a list of names, e.g. ["Alice", "Bob", "Carol"]
. If that array changes (maybe Bob leaves, and is replaced by Dave, so our list is now ["Alice, "Carol", "Dave"]
), we'll want to update the table. The easiest way to do this is by calling reloadData
on it. This has a couple of downsides: the transition isn't animated, and it'll cause your user to lose their scroll position if they've scrolled the table. The nicer way is to use the insertRowsAtIndexPaths:withRowAnimation
and deleteRowsAtIndexPaths:withRowAnimation
methods on UITableView
, but this requires you to figure out which index paths have changed in your array (in our example, you'd have to figure out that the row at index 1 should be removed, and a new row should be inserted at index 2 should then be added). If only we had a way to diff the previous value of our array with it's new value. Wait a minute.
When you wire up a TableViewDiffCalculator
to your UITableView
(or a CollectionViewDiffCalculator
to your UICollectionView
, it'll automatically calculate diffs and trigger the necessary animations on it whenever you change its sectionedValues
property. Neat, right? Notably, as of Dwifft 0.6, Dwifft will also figure out section insertions and deletions, as well as how to efficiently insert and delete rows across different sections, which is just so massively useful if you have a multi-section table. If you're currently using a <0.6 version of Dwifft and want to do this, read the 0.6 release notes.
Learn more about the history of Dwifft, and how it works, in this exciting video of a talk recorded at the Brooklyn Swift meetup in March 2017.
Contributions are welcome, with some caveats - please read the contributing guidelines before opening a PR to avoid wasting both our time.
Ok, that's it, there's nothing more here.
link |
Stars: 1847 |
Last commit: 5 weeks ago |
One of the most common feature requests since Dwifft launched has been the ability to more easily manage UITableView
/UICollectionView
s with multiple sections. I am beyond pleased to announce that Dwifft 0.6 gains this feature! This is done by modeling your data with a new struct titled SectionedValues<Section, Value>
. SectionedValues
is akin to an ordered dictionary - it contains an array of tuples, where the first element in the tuple represents the section itself, and the second element is an array of elements that should be contained at that section. If you were mapping this to, say, the Contacts app, you might imagine each Section
would be a letter of the alphabet, and the Value
s for each Section
would be the contacts whose name started with that letter. SectionedValues
can be a little annoying to construct, so they have some convenience initializers that can make that easier - you should read their documentation for more info.
TableViewDiffCalculator
and CollectionViewDiffCalculator
have been updated to reflect this. Instead of setting their rows
property to an array, you now set their sectionedValues
property to a, you guessed it, instance of SectionedValues
. This will now make calls to insertSections
and deleteSections
as well as insert{Rows|Items}
and delete{Rows|Items}
on your view. If you'd like to see this all in action, look at the example app, which has been updated to use all the new APIs.
There's some other good stuff in this release too. Dwifft is now tested with SwiftCheck, which yields much stronger guarantees around the correctness of the underlying diff algorithm. Dwifft 0.6 is also much faster and memory-efficient (like, an order of magnitude). I've also really beefed up the documentation - it's now autogenerated with jazzy and I'm pleased to say has 100% documentation coverage.
All of this was really hard to accomplish, especially in a way that didn't have garbage performance. I don't know of another diffing library that supports multi-section changes! I want to thank everyone who gave their feedback on the design and implementation.
Dwifft 0.6 has breaking changes. Sorry not sorry, this is a pre-1.0 library (even though it's like 2 years old) and I'm still settling on its shape. Notably, I have removed or slightly changed several types and methods.
TableViewDiffCalculator
and CollectionViewDiffCalculator
now use the aforementioned sectionedValues
property instead of rows
. If you would like the old behavior back, for the purposes of migrating, use SingleSectionTableViewDiffCalculator
/SingleSectionCollectionViewDiffCalculator
, which behave ~identically to how TableViewDiffCalculator
/CollectionViewDiffCalculator
used to work.Dwifft.diff(array1, array2)
instead, which behaves exactly the same way.Diff
struct has been removed - calls to diff
now just return an array of DiffStep
s (Diff
was just a thin wrapper around this array, and is no longer necessary).If you're having trouble migrating, drop me a line and I'll help.
Swiftpack is being maintained by Petr Pavlik | @ptrpavlik | @swiftpackco | API | Analytics