{"id":1047,"url":"https://github.com/ra1028/DifferenceKit","last_synced_at":"2025-08-06T16:32:13.920Z","repository":{"id":33084132,"uuid":"143424760","full_name":"ra1028/DifferenceKit","owner":"ra1028","description":"💻 A fast and flexible O(n) difference algorithm framework for Swift collection.","archived":false,"fork":false,"pushed_at":"2024-02-11T15:52:17.000Z","size":9185,"stargazers_count":3563,"open_issues_count":31,"forks_count":241,"subscribers_count":41,"default_branch":"master","last_synced_at":"2024-10-29T15:34:32.200Z","etag":null,"topics":["algorithm","changeset","collectionview","diff","difference","diffing","paul-heckel-algorithm","tableview"],"latest_commit_sha":null,"homepage":"https://ra1028.github.io/DifferenceKit","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ra1028.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"ra1028"}},"created_at":"2018-08-03T12:29:06.000Z","updated_at":"2024-10-28T08:56:31.000Z","dependencies_parsed_at":"2022-06-27T12:34:42.636Z","dependency_job_id":"fe182d71-a8b6-42dc-a2dd-844d4faea962","html_url":"https://github.com/ra1028/DifferenceKit","commit_stats":{"total_commits":223,"total_committers":16,"mean_commits":13.9375,"dds":"0.15246636771300448","last_synced_commit":"02ca1968b10305d4d6771f4005a7ce2c507a8539"},"previous_names":[],"tags_count":23,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ra1028%2FDifferenceKit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ra1028%2FDifferenceKit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ra1028%2FDifferenceKit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ra1028%2FDifferenceKit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ra1028","download_url":"https://codeload.github.com/ra1028/DifferenceKit/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228923747,"owners_count":17992573,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["algorithm","changeset","collectionview","diff","difference","diffing","paul-heckel-algorithm","tableview"],"created_at":"2024-01-05T20:15:37.797Z","updated_at":"2024-12-09T16:31:17.303Z","avatar_url":"https://github.com/ra1028.png","language":"Swift","readme":"\u003cimg src=\"https://raw.githubusercontent.com/ra1028/DifferenceKit/master/assets/sample.gif\" height=\"310\" align=\"right\"\u003e\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://raw.githubusercontent.com/ra1028/DifferenceKit/master/assets/logo.png\" width=\"500\"\u003e\n\u003c/p\u003e\n\n\u003cH4 align=\"center\"\u003e\nA fast and flexible O(n) difference algorithm framework for Swift collection.\u003c/br\u003e\nThe algorithm is optimized based on the Paul Heckel's algorithm.\n\u003c/H4\u003e\n\n\u003cp align=\"center\"\u003e\n\u003ca href=\"https://developer.apple.com/swift\"\u003e\u003cimg alt=\"Swift5\" src=\"https://img.shields.io/badge/language-Swift5-orange.svg\"/\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/ra1028/DifferenceKit/releases/latest\"\u003e\u003cimg alt=\"Release\" src=\"https://img.shields.io/github/release/ra1028/DifferenceKit.svg\"/\u003e\u003c/a\u003e\n\u003ca href=\"https://cocoapods.org/pods/DifferenceKit\"\u003e\u003cimg alt=\"CocoaPods\" src=\"https://img.shields.io/cocoapods/v/DifferenceKit.svg\"/\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/Carthage/Carthage\"\u003e\u003cimg alt=\"Carthage\" src=\"https://img.shields.io/badge/Carthage-compatible-yellow.svg\"/\u003e\u003c/a\u003e\n\u003ca href=\"https://swift.org/package-manager\"\u003e\u003cimg alt=\"Swift Package Manager\" src=\"https://img.shields.io/badge/SwiftPM-compatible-yellowgreen.svg\"/\u003e\u003c/a\u003e\n\u003c/br\u003e\n\u003ca href=\"https://github.com/ra1028/DifferenceKit/actions\"\u003e\u003cimg alt=\"CI Status\" src=\"https://github.com/ra1028/DifferenceKit/workflows/GitHub%20Actions/badge.svg\"/\u003e\u003c/a\u003e\n\u003ca href=\"https://developer.apple.com/\"\u003e\u003cimg alt=\"Platform\" src=\"https://img.shields.io/badge/platform-iOS%20%7C%20macOS%20%7C%20tvOS%20%7C%20watchOS%20%7C%20Linux-green.svg\"/\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/ra1028/DifferenceKit/blob/master/LICENSE\"\u003e\u003cimg alt=\"Lincense\" src=\"http://img.shields.io/badge/License-Apache%202.0-black.svg\"/\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cbr\u003e\n\n\u003cp align=\"center\"\u003e\nMade with ❤️ by \u003ca href=\"https://github.com/ra1028\"\u003eRyo Aoyama\u003c/a\u003e and \u003ca href=\"https://github.com/ra1028/DifferenceKit/graphs/contributors\"\u003eContributors\u003c/a\u003e\n\u003cbr clear=\"all\"\u003e\n\u003c/p\u003e\n\n---\n\n## Features\n\n💡 Fastest **O(n)** diffing algorithm optimized for Swift collection\n\n💡 Calculate diffs for batch updates of list UI in `UIKit`, `AppKit` and [Texture](https://github.com/TextureGroup/Texture)\n\n💡 Supports both linear and sectioned collection even if contains duplicates\n\n💡 Supports **all kind of diffs** for animated UI batch updates\n\n---\n\n## Algorithm\n\nThis is a diffing algorithm developed for [Carbon](https://github.com/ra1028/Carbon), works stand alone.  \nThe algorithm optimized based on the Paul Heckel's algorithm.  \nSee also his paper [\"A technique for isolating differences between files\"](https://dl.acm.org/citation.cfm?id=359467) released in 1978.  \nIt allows all kind of diffs to be calculated in linear time **O(n)**.  \n[RxDataSources](https://github.com/RxSwiftCommunity/RxDataSources) and [IGListKit](https://github.com/Instagram/IGListKit) are also implemented based on his algorithm.  \n\nHowever, in `performBatchUpdates` of `UITableView`, `UICollectionView`, etc, there are combinations of diffs that cause crash when applied simultaneously.  \nTo 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.\n\nImplementation is [here](https://github.com/ra1028/DifferenceKit/blob/master/Sources/Algorithm.swift).\n\n---\n\n## Getting Started\n\n- [API Documentation](https://ra1028.github.io/DifferenceKit)\n- [Example Apps](https://github.com/ra1028/DifferenceKit/blob/master/Examples)\n- [Benchmark](https://github.com/ra1028/DifferenceKit/blob/master/Benchmark)\n- [Playground](https://github.com/ra1028/DifferenceKit/blob/master/DifferenceKit.playground/Contents.swift)\n\n## Basic Usage\n\nThe type of the element that to take diffs must be conform to the `Differentiable` protocol.  \nThe `differenceIdentifier`'s type is generic associated type:\n```swift\nstruct User: Differentiable {\n    let id: Int\n    let name: String\n\n    var differenceIdentifier: Int {\n        return id\n    }\n\n    func isContentEqual(to source: User) -\u003e Bool {\n        return name == source.name\n    }\n}\n```\n\nIn 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.\n\nThere are default implementations of `Differentiable` for the types that conforming to `Equatable` or `Hashable`：\n```swift\n// If `Self` conforming to `Hashable`.\nvar differenceIdentifier: Self {\n    return self\n}\n\n// If `Self` conforming to `Equatable`.\nfunc isContentEqual(to source: Self) -\u003e Bool {\n    return self == source\n}\n```\nTherefore, you can simply:\n```swift\nextension String: Differentiable {}\n```\n\nCalculate the diffs by creating `StagedChangeset` from two collections of elements conforming to `Differentiable`:\n```swift\nlet source = [\n    User(id: 0, name: \"Vincent\"),\n    User(id: 1, name: \"Jules\")\n]\nlet target = [\n    User(id: 1, name: \"Jules\"),\n    User(id: 0, name: \"Vincent\"),\n    User(id: 2, name: \"Butch\")\n]\n\nlet changeset = StagedChangeset(source: source, target: target)\n```\n\nIf you want to include multiple types conforming to `Differentiable` in the collection, use `AnyDifferentiable`:\n```swift\nlet source = [\n    AnyDifferentiable(\"A\"),\n    AnyDifferentiable(User(id: 0, name: \"Vincent\"))\n]\n```\n\nIn the case of sectioned collection, the section itself must have a unique identifier and be able to compare whether there is an update.  \nSo each section must conforming to `DifferentiableSection` protocol, but in most cases you can use `ArraySection` that general type conforming to it.  \n`ArraySection` requires a model conforming to `Differentiable` for diffing from other sections:\n```swift\nenum Model: Differentiable {\n    case a, b, c\n}\n\nlet source: [ArraySection\u003cModel, String\u003e] = [\n    ArraySection(model: .a, elements: [\"A\", \"B\"]),\n    ArraySection(model: .b, elements: [\"C\"])\n]\nlet target: [ArraySection\u003cModel, String\u003e] = [\n    ArraySection(model: .c, elements: [\"D\", \"E\"]),\n    ArraySection(model: .a, elements: [\"A\"]),\n    ArraySection(model: .b, elements: [\"B\", \"C\"])\n]\n\nlet changeset = StagedChangeset(source: source, target: target)\n```\n\nYou can perform diffing batch updates of `UITableView` and `UICollectionView` using the created `StagedChangeset`.  \n\n⚠️ **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:\n\n```swift\ntableView.reload(using: changeset, with: .fade) { data in\n    dataSource.data = data\n}\n```\n\nBatch updates using too large amount of diffs may adversely affect to performance.  \nReturning `true` with `interrupt` closure then falls back to `reloadData`:\n```swift\ncollectionView.reload(using: changeset, interrupt: { $0.changeCount \u003e 100 }) { data in\n    dataSource.data = data\n}\n```\n\n\u003cH3 align=\"center\"\u003e\n\u003ca href=\"https://ra1028.github.io/DifferenceKit\"\u003e[See More Usage]\u003c/a\u003e\n\u003c/H3\u003e\n\n---\n\n## Comparison with Other Frameworks\n\nMade a fair comparison as much as possible in performance and features with other **popular** and **awesome** frameworks.  \nThis does **NOT** determine superiority or inferiority of the frameworks.  \nI know that each framework has different benefits.  \nThe frameworks and its version that compared is below.  \n\n- [DifferenceKit](https://github.com/ra1028/DifferenceKit) - master\n- [RxDataSources](https://github.com/RxSwiftCommunity/RxDataSources) ([Differentiator](https://github.com/RxSwiftCommunity/RxDataSources/tree/master/Sources/Differentiator)) - 4.0.1\n- [FlexibleDiff](https://github.com/RACCommunity/FlexibleDiff) - 0.0.8\n- [IGListKit](https://github.com/Instagram/IGListKit) - 3.4.0\n- [DeepDiff](https://github.com/onmyway133/DeepDiff) - 2.2.0\n- [Differ](https://github.com/tonyarnold/Differ) ([Diff.swift](https://github.com/wokalski/Diff.swift)) - 1.4.3\n- [Dwifft](https://github.com/jflinter/Dwifft) - 0.9\n- [Swift.CollectionDifference](https://developer.apple.com/documentation/swift/collectiondifference) - Swift 5.1\n\n### Performance Comparison\n\nBenchmark project is [here](https://github.com/ra1028/DifferenceKit/blob/master/Benchmark).  \nPerformance was mesured by code compiled using `Xcode11.1` and `Swift 5.1` with `-O` optimization and run on `iPhone11 Pro simulator`.  \nUse `Foundation.UUID` as an element of collections.  \n\n#### - From 5,000 elements to 1,000 deleted, 1,000 inserted and 200 shuffled\n\n|                          |Time(sec)                 |\n|:-------------------------|-------------------------:|\n|DifferenceKit             |`0.0019`                  |\n|RxDataSources             |`0.0074`                  |\n|IGListKit                 |`0.0346`                  |\n|FlexibleDiff              |`0.0161`                  |\n|DeepDiff                  |`0.0373`                  |\n|Differ                    |`1.0581`                  |\n|Dwifft                    |`0.4732`                  |\n|Swift.CollectionDifference|`0.0620`                  |\n\n#### - From 100,000 elements to 10,000 deleted, 10,000 inserted and 2,000 shuffled\n\n|                          |Time(sec)                 |\n|:-------------------------|-------------------------:|\n|DifferenceKit             |`0.0348`                  |\n|RxDataSources             |`0.1024`                  |\n|IGListKit                 |`0.7002`                  |\n|FlexibleDiff              |`0.2189`                  |\n|DeepDiff                  |`0.5537`                  |\n|Differ                    |`153.8007`                |\n|Dwifft                    |`187.1341`                |\n|Swift.CollectionDifference|`5.0281`                  |\n\n### Features Comparison\n\n#### - Algorithm\n\n|                          |Base algorithm|Order|\n|:-------------------------|-------------:|----:|\n|DifferenceKit             |Heckel        |O(N) |\n|RxDataSources             |Heckel        |O(N) |\n|FlexibleDiff              |Heckel        |O(N) |\n|IGListKit                 |Heckel        |O(N) |\n|DeepDiff                  |Heckel        |O(N) |\n|Differ                    |Myers         |O(ND)|\n|Dwifft                    |Myers         |O(ND)|\n|Swift.CollectionDifference|Myers         |O(ND)|\n\n\\* [**Heckel algorithm**](https://dl.acm.org/citation.cfm?id=359467)  \n\\* [**Myers algorithm**](http://www.xmailserver.org/diff2.pdf)  \n\n#### - Supported Collection\n\n|                          |Linear|Sectioned|Duplicate element/section|\n|:-------------------------|:----:|:-------:|:-----------------------:|\n|DifferenceKit             |✅    |✅       |✅                      |\n|RxDataSources             |❌    |✅       |❌                      |\n|FlexibleDiff              |✅    |✅       |✅                      |\n|IGListKit                 |✅    |❌       |✅                      |\n|DeepDiff                  |✅    |❌       |✅                      |\n|Differ                    |✅    |✅       |✅                      |\n|Dwifft                    |✅    |✅       |✅                      |\n|Swift.CollectionDifference|✅    |❌       |✅                      |\n\n\\* **Linear** means 1-dimensional collection  \n\\* **Sectioned** means 2-dimensional collection  \n\n#### - Supported Element Diff\n\n|                          |Delete|Insert|Move|Reload|Move across sections|\n|:-------------------------|:----:|:----:|:--:|:----:|:------------------:|\n|DifferenceKit             |✅    |✅    |✅ |✅    |✅                  |\n|RxDataSources             |✅    |✅    |✅ |✅    |✅                  |\n|FlexibleDiff              |✅    |✅    |✅ |✅    |❌                  |\n|IGListKit                 |✅    |✅    |✅ |✅    |❌                  |\n|DeepDiff                  |✅    |✅    |✅ |✅    |❌                  |\n|Differ                    |✅    |✅    |✅ |❌    |❌                  |\n|Dwifft                    |✅    |✅    |❌ |❌    |❌                  |\n|Swift.CollectionDifference|✅    |✅    |✅ |❌    |❌                  |\n\n#### - Supported Section Diff\n\n|                          |Delete|Insert|Move|Reload|\n|:-------------------------|:----:|:----:|:--:|:----:|\n|DifferenceKit             |✅    |✅    |✅ |✅    |\n|RxDataSources             |✅    |✅    |✅ |❌    |\n|FlexibleDiff              |✅    |✅    |✅ |✅    |\n|IGListKit                 |❌    |❌    |❌ |❌    |\n|DeepDiff                  |❌    |❌    |❌ |❌    |\n|Differ                    |✅    |✅    |✅ |❌    |\n|Dwifft                    |✅    |✅    |❌ |❌    |\n|Swift.CollectionDifference|❌    |❌    |❌ |❌    |\n\n---\n\n## Requirements\n\n- Swift 4.2+\n- iOS 9.0+\n- tvOS 9.0+\n- OS X 10.9+\n- watchOS 2.0+ (only algorithm)\n\n---\n\n## Installation\n\n### [CocoaPods](https://cocoapods.org/)\n\nTo use only algorithm without extensions for UI, add the following to your `Podfile`:\n```ruby\npod 'DifferenceKit/Core'\n```\n\n#### iOS / tvOS\n\nTo use DifferenceKit with UIKit extension, add the following to your `Podfile`:\n```ruby\npod 'DifferenceKit'\n```\nor\n```ruby\npod 'DifferenceKit/UIKitExtension'\n```\n\n#### macOS\n\nTo use DifferenceKit with AppKit extension, add the following to your `Podfile`:\n```ruby\npod 'DifferenceKit/AppKitExtension'\n```\n\n#### watchOS\n\nThere is no UI extension for watchOS.  \nTo use only algorithm without extensions for UI, add the following to your `Podfile`:\n```ruby\npod 'DifferenceKit/Core'\n```\n\n### [Carthage](https://github.com/Carthage/Carthage)\n\nAdd the following to your `Cartfile`:\n```ruby\ngithub \"ra1028/DifferenceKit\"\n```\n\n### [Swift Package Manager for Apple platforms](https://developer.apple.com/documentation/xcode/adding_package_dependencies_to_your_app)\n\nSelect Xcode menu `File \u003e Swift Packages \u003e Add Package Dependency` and enter repository URL with GUI.  \n```\nRepository: https://github.com/ra1028/DifferenceKit\n```\n\n### [Swift Package Manager](https://swift.org/package-manager/)\n\nAdd the following to the dependencies of your `Package.swift`:\n```swift\n.package(url: \"https://github.com/ra1028/DifferenceKit.git\", from: \"version\")\n```\n\n---\n\n## Contribution\n\nPull requests, bug reports and feature requests are welcome 🚀  \nPlease see the [CONTRIBUTING](https://github.com/ra1028/DifferenceKit/blob/master/CONTRIBUTING.md) file for learn how to contribute to DifferenceKit. \n \n---\n\n## Credit\n\n#### Bibliography\nDifferenceKit was developed with reference to the following excellent materials and framework.  \n\n- [A technique for isolating differences between files](https://dl.acm.org/citation.cfm?id=359467) (by [Paul Heckel](https://dl.acm.org/author_page.cfm?id=81100051772))\n- [DifferenceAlgorithmComparison](https://github.com/horita-yuya/DifferenceAlgorithmComparison) (by [@horita-yuya](https://github.com/horita-yuya))\n\n#### OSS using DifferenceKit\nThe list of the awesome OSS which uses this library. They also help to understanding how to use DifferenceKit.  \n\n- [Carbon](https://github.com/ra1028/Carbon) (by [@ra1028](https://github.com/ra1028))\n- [DiffableDataSources](https://github.com/ra1028/DiffableDataSources) (by [@ra1028](https://github.com/ra1028))\n- [Rocket.Chat.iOS](https://github.com/RocketChat/Rocket.Chat.iOS) (by [RocketChat](https://github.com/RocketChat))\n- [wire-ios](https://github.com/wireapp/wire-ios) (by [Wire Swiss GmbH](https://github.com/wireapp))\n- [ReactiveLists](https://github.com/plangrid/ReactiveLists) (by [PlanGrid](https://github.com/plangrid))\n- [ReduxMovieDB](https://github.com/cardoso/ReduxMovieDB) (by [@cardoso](https://github.com/cardoso))\n- [TetrisDiffingCompetition](https://github.com/skagedal/TetrisDiffingCompetition) (by [@skagedal](https://github.com/skagedal))\n\n#### Other diffing libraries\nI respect and ️❤️ all libraries involved in diffing.  \n\n- [RxDataSources](https://github.com/RxSwiftCommunity/RxDataSources) (by [@kzaher](https://github.com/kzaher), [RxSwift Community](https://github.com/RxSwiftCommunity))\n- [IGListKit](https://github.com/Instagram/IGListKit) (by [Instagram](https://github.com/Instagram))\n- [FlexibleDiff](https://github.com/RACCommunity/FlexibleDiff) (by [@andersio](https://github.com/andersio), [RACCommunity](https://github.com/RACCommunity))\n- [DeepDiff](https://github.com/onmyway133/DeepDiff) (by [@onmyway133](https://github.com/onmyway133))\n- [Differ](https://github.com/tonyarnold/Differ) (by [@tonyarnold](https://github.com/tonyarnold))\n- [Dwifft](https://github.com/jflinter/Dwifft) (by [@jflinter](https://github.com/jflinter))\n- [Changeset](https://github.com/osteslag/Changeset) (by [@osteslag](https://github.com/osteslag))\n\n---\n\n## License\nDifferenceKit is released under the [Apache 2.0 License](https://github.com/ra1028/DifferenceKit/blob/master/LICENSE).\n","funding_links":["https://github.com/sponsors/ra1028"],"categories":["Data Structures / Algorithms","Libs","HarmonyOS","Swift","Utility [🔝](#readme)","iOS","tableview"],"sub_categories":["Getting Started","Utility","Windows Manager","Linter","DataSources"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fra1028%2FDifferenceKit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fra1028%2FDifferenceKit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fra1028%2FDifferenceKit/lists"}