DifferenceKit
💻 A fast and flexible O(n) difference algorithm framework for Swift collection.
Top Related Projects
Swift Diff
🦀Amazingly incredible extraordinary lightning fast diffing in Swift
CryptoSwift is a growing collection of standard and secure cryptographic algorithms implemented in Swift
Quick Overview
DifferenceKit is a Swift library for calculating differences between two collections and applying batch updates to a UITableView/UICollectionView. It provides a fast and memory-efficient solution for handling data changes in iOS applications, making it easier to implement smooth animations when updating user interfaces.
Pros
- High performance and memory efficiency
- Easy integration with UITableView and UICollectionView
- Supports both linear (sections and items) and tree structures
- Comprehensive unit test coverage
Cons
- Limited to iOS and Swift development
- May have a learning curve for developers new to diffing algorithms
- Requires manual integration (not available through Swift Package Manager)
- Documentation could be more extensive for advanced use cases
Code Examples
- Calculating differences between two arrays:
let old = ["A", "B", "C"]
let new = ["A", "C", "D"]
let changeset = StagedChangeset(source: old, target: new)
print(changeset)
- Applying changes to a UITableView:
tableView.reload(using: changeset, with: .automatic) { data in
self.data = data
}
- Handling section and item changes:
let changeset = StagedChangeset(source: oldSections, target: newSections)
collectionView.reload(using: changeset, with: .automatic) { data in
self.data = data
}
Getting Started
- Add DifferenceKit to your project using CocoaPods:
pod 'DifferenceKit'
- Import the library in your Swift file:
import DifferenceKit
- Use the
StagedChangeset
to calculate differences:
let changeset = StagedChangeset(source: oldData, target: newData)
- Apply changes to your UITableView or UICollectionView:
tableView.reload(using: changeset, with: .automatic) { data in
self.data = data
}
Competitor Comparisons
Swift Diff
Pros of Dwifft
- Simpler implementation, making it easier to understand and modify
- Supports both Swift and Objective-C
- Includes a specialized version for UITableView diffing
Cons of Dwifft
- Generally slower performance compared to DifferenceKit
- Less comprehensive feature set
- Not actively maintained (last commit was in 2019)
Code Comparison
DifferenceKit:
let changeset = StagedChangeset(source: oldItems, target: newItems)
tableView.reload(using: changeset) { data in
dataSource = data
}
Dwifft:
let diff = Dwifft.diff(oldItems, newItems)
tableView.applyDiff(diff, withAnimation: .automatic)
Both libraries aim to efficiently calculate and apply differences between collections, but DifferenceKit offers more advanced features and better performance. Dwifft provides a simpler API and supports Objective-C, which may be beneficial for some projects. However, DifferenceKit is more actively maintained and optimized for performance, making it a better choice for larger and more complex applications.
🦀Amazingly incredible extraordinary lightning fast diffing in Swift
Pros of DeepDiff
- Simpler API and easier to use for basic diffing tasks
- Supports diffing of nested structures (arrays and dictionaries)
- Lightweight and has fewer dependencies
Cons of DeepDiff
- Less performant for large datasets compared to DifferenceKit
- Lacks advanced features like batch updates and sectioned data support
- Not as actively maintained (last update was over 2 years ago)
Code Comparison
DeepDiff:
let changes = diff(old: oldItems, new: newItems)
tableView.reload(changes: changes, updateData: {
self.items = newItems
})
DifferenceKit:
let changeset = StagedChangeset(source: oldItems, target: newItems)
tableView.reload(using: changeset, with: .automatic) { data in
self.items = data
}
Both libraries provide similar functionality for basic diffing and updating UI components. DifferenceKit offers more advanced features and better performance for complex scenarios, while DeepDiff is simpler and easier to use for basic tasks. The choice between them depends on the specific requirements of your project and the complexity of your data structures.
CryptoSwift is a growing collection of standard and secure cryptographic algorithms implemented in Swift
Pros of CryptoSwift
- Comprehensive cryptographic toolkit with various algorithms and functions
- Pure Swift implementation, ensuring compatibility across Apple platforms
- Active development and regular updates
Cons of CryptoSwift
- Performance may be slower compared to native C implementations
- Larger codebase and potential impact on app size
- Requires careful usage to ensure proper security implementation
Code Comparison
While a direct code comparison isn't relevant due to the different purposes of these libraries, here's a brief example of how each might be used:
DifferenceKit:
let changeset = StagedChangeset(source: oldItems, target: newItems)
tableView.reload(using: changeset) { data in
dataSource = data
}
CryptoSwift:
let encrypted = try AES(key: "secret", iv: "iv").encrypt("hello")
let decrypted = try AES(key: "secret", iv: "iv").decrypt(encrypted)
DifferenceKit focuses on efficient collection updates, while CryptoSwift provides cryptographic operations. The choice between them depends on the specific needs of your project, whether it's optimizing UI updates or implementing security features.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
A fast and flexible O(n) difference algorithm framework for Swift collection.
The algorithm is optimized based on the Paul Heckel's algorithm.
Made with â¤ï¸ by Ryo Aoyama and Contributors
Features
ð¡ Fastest O(n) diffing algorithm optimized for Swift collection
ð¡ Calculate diffs for batch updates of list UI in UIKit
, AppKit
and Texture
ð¡ Supports both linear and sectioned collection even if contains duplicates
ð¡ Supports all kind of diffs for animated UI batch updates
Algorithm
This is a diffing algorithm developed for Carbon, works stand alone.
The algorithm optimized based on the Paul Heckel's algorithm.
See also his paper "A technique for isolating differences between files" released in 1978.
It allows all kind of diffs to be calculated in linear time O(n).
RxDataSources and IGListKit are also implemented based on his algorithm.
However, in performBatchUpdates
of UITableView
, UICollectionView
, etc, there are combinations of diffs that cause crash when applied simultaneously.
To solve this problem, DifferenceKit
takes an approach of split the set of diffs at the minimal stages that can be perform batch updates with no crashes.
Implementation is here.
Getting Started
Basic Usage
The type of the element that to take diffs must be conform to the Differentiable
protocol.
The differenceIdentifier
's type is generic associated type:
struct User: Differentiable {
let id: Int
let name: String
var differenceIdentifier: Int {
return id
}
func isContentEqual(to source: User) -> Bool {
return name == source.name
}
}
In the case of definition above, id
uniquely identifies the element and get to know the user updated by comparing equality of name
of the elements in source and target.
There are default implementations of Differentiable
for the types that conforming to Equatable
or Hashable
ï¼
// If `Self` conforming to `Hashable`.
var differenceIdentifier: Self {
return self
}
// If `Self` conforming to `Equatable`.
func isContentEqual(to source: Self) -> Bool {
return self == source
}
Therefore, you can simply:
extension String: Differentiable {}
Calculate the diffs by creating StagedChangeset
from two collections of elements conforming to Differentiable
:
let source = [
User(id: 0, name: "Vincent"),
User(id: 1, name: "Jules")
]
let target = [
User(id: 1, name: "Jules"),
User(id: 0, name: "Vincent"),
User(id: 2, name: "Butch")
]
let changeset = StagedChangeset(source: source, target: target)
If you want to include multiple types conforming to Differentiable
in the collection, use AnyDifferentiable
:
let source = [
AnyDifferentiable("A"),
AnyDifferentiable(User(id: 0, name: "Vincent"))
]
In the case of sectioned collection, the section itself must have a unique identifier and be able to compare whether there is an update.
So each section must conforming to DifferentiableSection
protocol, but in most cases you can use ArraySection
that general type conforming to it.
ArraySection
requires a model conforming to Differentiable
for diffing from other sections:
enum Model: Differentiable {
case a, b, c
}
let source: [ArraySection<Model, String>] = [
ArraySection(model: .a, elements: ["A", "B"]),
ArraySection(model: .b, elements: ["C"])
]
let target: [ArraySection<Model, String>] = [
ArraySection(model: .c, elements: ["D", "E"]),
ArraySection(model: .a, elements: ["A"]),
ArraySection(model: .b, elements: ["B", "C"])
]
let changeset = StagedChangeset(source: source, target: target)
You can perform diffing batch updates of UITableView
and UICollectionView
using the created StagedChangeset
.
â ï¸ Don't forget to synchronously update the data referenced by the data-source, with the data passed in the setData
closure. The diffs are applied in stages, and failing to do so is bound to create a crash:
tableView.reload(using: changeset, with: .fade) { data in
dataSource.data = data
}
Batch updates using too large amount of diffs may adversely affect to performance.
Returning true
with interrupt
closure then falls back to reloadData
:
collectionView.reload(using: changeset, interrupt: { $0.changeCount > 100 }) { data in
dataSource.data = data
}
[See More Usage]
Comparison with Other Frameworks
Made a fair comparison as much as possible in performance and features with other popular and awesome frameworks.
This does NOT determine superiority or inferiority of the frameworks.
I know that each framework has different benefits.
The frameworks and its version that compared is below.
- DifferenceKit - master
- RxDataSources (Differentiator) - 4.0.1
- FlexibleDiff - 0.0.8
- IGListKit - 3.4.0
- DeepDiff - 2.2.0
- Differ (Diff.swift) - 1.4.3
- Dwifft - 0.9
- Swift.CollectionDifference - Swift 5.1
Performance Comparison
Benchmark project is here.
Performance was mesured by code compiled using Xcode11.1
and Swift 5.1
with -O
optimization and run on iPhone11 Pro simulator
.
Use Foundation.UUID
as an element of collections.
- From 5,000 elements to 1,000 deleted, 1,000 inserted and 200 shuffled
Time(sec) | |
---|---|
DifferenceKit | 0.0019 |
RxDataSources | 0.0074 |
IGListKit | 0.0346 |
FlexibleDiff | 0.0161 |
DeepDiff | 0.0373 |
Differ | 1.0581 |
Dwifft | 0.4732 |
Swift.CollectionDifference | 0.0620 |
- From 100,000 elements to 10,000 deleted, 10,000 inserted and 2,000 shuffled
Time(sec) | |
---|---|
DifferenceKit | 0.0348 |
RxDataSources | 0.1024 |
IGListKit | 0.7002 |
FlexibleDiff | 0.2189 |
DeepDiff | 0.5537 |
Differ | 153.8007 |
Dwifft | 187.1341 |
Swift.CollectionDifference | 5.0281 |
Features Comparison
- Algorithm
Base algorithm | Order | |
---|---|---|
DifferenceKit | Heckel | O(N) |
RxDataSources | Heckel | O(N) |
FlexibleDiff | Heckel | O(N) |
IGListKit | Heckel | O(N) |
DeepDiff | Heckel | O(N) |
Differ | Myers | O(ND) |
Dwifft | Myers | O(ND) |
Swift.CollectionDifference | Myers | O(ND) |
* Heckel algorithm
* Myers algorithm
- Supported Collection
Linear | Sectioned | Duplicate element/section | |
---|---|---|---|
DifferenceKit | â | â | â |
RxDataSources | â | â | â |
FlexibleDiff | â | â | â |
IGListKit | â | â | â |
DeepDiff | â | â | â |
Differ | â | â | â |
Dwifft | â | â | â |
Swift.CollectionDifference | â | â | â |
* Linear means 1-dimensional collection
* Sectioned means 2-dimensional collection
- Supported Element Diff
Delete | Insert | Move | Reload | Move across sections | |
---|---|---|---|---|---|
DifferenceKit | â | â | â | â | â |
RxDataSources | â | â | â | â | â |
FlexibleDiff | â | â | â | â | â |
IGListKit | â | â | â | â | â |
DeepDiff | â | â | â | â | â |
Differ | â | â | â | â | â |
Dwifft | â | â | â | â | â |
Swift.CollectionDifference | â | â | â | â | â |
- Supported Section Diff
Delete | Insert | Move | Reload | |
---|---|---|---|---|
DifferenceKit | â | â | â | â |
RxDataSources | â | â | â | â |
FlexibleDiff | â | â | â | â |
IGListKit | â | â | â | â |
DeepDiff | â | â | â | â |
Differ | â | â | â | â |
Dwifft | â | â | â | â |
Swift.CollectionDifference | â | â | â | â |
Requirements
- Swift 4.2+
- iOS 9.0+
- tvOS 9.0+
- OS X 10.9+
- watchOS 2.0+ (only algorithm)
Installation
CocoaPods
To use only algorithm without extensions for UI, add the following to your Podfile
:
pod 'DifferenceKit/Core'
iOS / tvOS
To use DifferenceKit with UIKit extension, add the following to your Podfile
:
pod 'DifferenceKit'
or
pod 'DifferenceKit/UIKitExtension'
macOS
To use DifferenceKit with AppKit extension, add the following to your Podfile
:
pod 'DifferenceKit/AppKitExtension'
watchOS
There is no UI extension for watchOS.
To use only algorithm without extensions for UI, add the following to your Podfile
:
pod 'DifferenceKit/Core'
Carthage
Add the following to your Cartfile
:
github "ra1028/DifferenceKit"
Swift Package Manager for Apple platforms
Select Xcode menu File > Swift Packages > Add Package Dependency
and enter repository URL with GUI.
Repository: https://github.com/ra1028/DifferenceKit
Swift Package Manager
Add the following to the dependencies of your Package.swift
:
.package(url: "https://github.com/ra1028/DifferenceKit.git", from: "version")
Contribution
Pull requests, bug reports and feature requests are welcome ð
Please see the CONTRIBUTING file for learn how to contribute to DifferenceKit.
Credit
Bibliography
DifferenceKit was developed with reference to the following excellent materials and framework.
- A technique for isolating differences between files (by Paul Heckel)
- DifferenceAlgorithmComparison (by @horita-yuya)
OSS using DifferenceKit
The list of the awesome OSS which uses this library. They also help to understanding how to use DifferenceKit.
- Carbon (by @ra1028)
- DiffableDataSources (by @ra1028)
- Rocket.Chat.iOS (by RocketChat)
- wire-ios (by Wire Swiss GmbH)
- ReactiveLists (by PlanGrid)
- ReduxMovieDB (by @cardoso)
- TetrisDiffingCompetition (by @skagedal)
Other diffing libraries
I respect and ï¸â¤ï¸ all libraries involved in diffing.
- RxDataSources (by @kzaher, RxSwift Community)
- IGListKit (by Instagram)
- FlexibleDiff (by @andersio, RACCommunity)
- DeepDiff (by @onmyway133)
- Differ (by @tonyarnold)
- Dwifft (by @jflinter)
- Changeset (by @osteslag)
License
DifferenceKit is released under the Apache 2.0 License.
Top Related Projects
Swift Diff
🦀Amazingly incredible extraordinary lightning fast diffing in Swift
CryptoSwift is a growing collection of standard and secure cryptographic algorithms implemented in Swift
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot