{"id":2870,"url":"https://github.com/kazuhiro4949/EditDistance","last_synced_at":"2025-08-03T12:31:29.870Z","repository":{"id":56909856,"uuid":"82382752","full_name":"kazuhiro4949/EditDistance","owner":"kazuhiro4949","description":"Incremental update tool to UITableView and UICollectionView","archived":false,"fork":false,"pushed_at":"2019-06-15T03:58:43.000Z","size":169,"stargazers_count":90,"open_issues_count":2,"forks_count":5,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-11-26T12:11:38.410Z","etag":null,"topics":["algorithm","carthage","cocoapods","editdistance","ios","swift","uicollectionview","uitableview"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kazuhiro4949.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-02-18T11:43:17.000Z","updated_at":"2023-12-07T17:29:18.000Z","dependencies_parsed_at":"2022-08-20T20:20:49.773Z","dependency_job_id":null,"html_url":"https://github.com/kazuhiro4949/EditDistance","commit_stats":null,"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kazuhiro4949%2FEditDistance","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kazuhiro4949%2FEditDistance/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kazuhiro4949%2FEditDistance/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kazuhiro4949%2FEditDistance/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kazuhiro4949","download_url":"https://codeload.github.com/kazuhiro4949/EditDistance/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228543158,"owners_count":17934433,"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","carthage","cocoapods","editdistance","ios","swift","uicollectionview","uitableview"],"created_at":"2024-01-05T20:16:25.031Z","updated_at":"2024-12-07T00:31:02.708Z","avatar_url":"https://github.com/kazuhiro4949.png","language":"Swift","funding_links":[],"categories":["UI"],"sub_categories":["Table View / Collection View","Other free courses"],"readme":"![editdistancelogo](https://user-images.githubusercontent.com/18320004/31609649-04d95c24-b2b0-11e7-96a9-6c8dc3989377.png)\n\n[![Platform](https://img.shields.io/cocoapods/p/EditDistance.svg?style=flat)](http://cocoapods.org/pods/EditDistance)\n[![Platform](https://img.shields.io/badge/platform-tvos-lightgrey.svg)](http://cocoapods.org/pods/EditDistance)\n![Swift 4.0](https://img.shields.io/badge/Swift-4.0-orange.svg)\n![Swift 3.2](https://img.shields.io/badge/Swift-3.2-orange.svg)\n[![License](https://img.shields.io/cocoapods/l/EditDistance.svg?style=flat)](http://cocoapods.org/pods/EditDistance)\n[![Version](https://img.shields.io/cocoapods/v/EditDistance.svg?style=flat)](http://cocoapods.org/pods/EditDistance)\n[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)\n\nEditDistance is one of the incremental update tool for UITableView and UICollectionView. \n\nThe followings show how this library update UI. They generate the random items and update their UI incrementally.\n\n| UITableView | UICollectionView |\n|---|---|\n| ![tableview](https://cloud.githubusercontent.com/assets/18320004/23104148/adbfb22c-f70b-11e6-80bc-97fb1bac7bbc.gif)  | ![collectionview 1](https://cloud.githubusercontent.com/assets/18320004/23104147/ab1a6d00-f70b-11e6-921b-e328153306fd.gif)  |\n\n# What's this?\nThis library pipelines the process to update UITableView and UICollectionView. It is so difficult to update them incrementally, because iOS app developers need to manage differences between the two DataSources.\n\nIf you update items for DataSource:\n```swift\n// dataSource has [\"Francis Elton\", \"Stanton Denholm\", \"Arledge Camden\", \"Farland Ridley\", \"Alex Helton\"]\nvar nextDataSource = dataSource\n\n// insertion and deletion to data source\nnextDataSource.remove(at: 2)\nnextDataSource.insert(\"Woodruff Chester\", at: 1)\nnextDataSource.insert(\"Eduard Colby\", at: 3)\n```\n\nTypical code:\n\n```swift\n// You have to update UITableView according to array's diff.\ndataSource = nextDataSource\ntableView.beginUpdates()\ntableView.deleteRows(at: [IndexPath(row: 2, section: 0)], with: .fade)\ntableView.insertRows(at: [IndexPath(row: 1, section: 0), IndexPath(row: 3, section: 0)], with: .fade)\ntableView.endUpdates()\n```\n\nEditDistance takes on that task:\n\n```swift\n// You don't need to write insertion and deletion.\nlet container = dataSource.diff.compare(to: nextDataSource)\ndataSource = nextDataSource\ntableView.diff.reload(to: container) \n```\n\nAll you need is to make the updated array.\n\nYou don't have to manage how to update incrementally. That enables to pileline the process.\n\n# How dose it work?\nEditDistance calculates the difference and converts it into an incremental update of UITableView or UICollectionView.\n\nThe difference is based on [**Edit Distance Algorithm**](https://en.wikipedia.org/wiki/Edit_distance). There are many ways to calculate it and almost all of them nearly run in linear time.\n\n- Dynamic Programming (*O(NM)*)\n- Mayer's Algorithm (*O(ND)*)\n- Wu's Algorithm (*O(NP)*)\n- etc.\n\n*N* and *M* are sequence sizes of each array. *D* is edit distance and *P* is the number of deletion.\n\nIn our context, Wu's Algorithm seems to be the best algorithm. It has better performance than the others when your app has many items and adds (or deletes) a few items. (e.g. autopager, access history and notification)\n\n# Pros and Cons\nCalculation in this library is not always reasonable to update UI. I recommend that your app calculates edit distance in sub-thread and update UI in main-thread.\n\n# Feature\n- [x] You don't need to calculate diff manually.\n- [x] You can choose any diff algorithm as you like.\n- [x] You don't need to call [reloadRows(at:with:)](https://developer.apple.com/reference/uikit/uitableview/1614935-reloadrows) and [performBatchUpdates(_:completion:)](https://developer.apple.com/reference/uikit/uicollectionview/1618045-performbatchupdates) anymore.\n- [x] minimum implimentation for incremental update\n\n# Requirements\n- iOS 8.0+\n- Xcode 8.1+\n- Swift 3.0+\n\n# Installation\n\n### Carthage\n\n+ Install Carthage from Homebrew\n```\n\u003e ruby -e \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)\"\n\u003e brew update\n\u003e brew install carthage\n```\n+ Move your project dir and create Cartfile\n```\n\u003e touch Cartfile\n```\n+ add the following line to Cartfile\n```\ngithub \"kazuhiro4949/EditDistance\"\n```\n+ Create framework\n```\n\u003e carthage update --platform iOS\n```\n\n+ In Xcode, move to \"Genera \u003e Build Phase \u003e Linked Frameworks and Library\"\n+ Add the framework to your project\n+ Add a new run script and put the following code\n```\n/usr/local/bin/carthage copy-frameworks\n```\n+ Click \"+\" at Input file and Add the framework path\n```\n$(SRCROOT)/Carthage/Build/iOS/EditDistance.framework\n```\n+ Write Import statement on your source file\n```\nImport EditDistance\n```\n\n### CocoaPods\n+ Install CocoaPods\n```\n\u003e gem install cocoapods\n\u003e pod setup\n```\n+ Create Podfile\n```\n\u003e pod init\n```\n+ Edit Podfile\n```ruby\n# Uncomment this line to define a global platform for your project\nplatform :ios, '8.0'  # add\nuse_framework!  # add\n\ntarget 'MyAppName' do\n  pod 'EditDistance' # add\nend\n\ntarget 'MyAppTests' do\n\nend\n\ntarget 'MyAppUITests'\n```\n\n+ Install\n\n```\n\u003e pod install\n```\nopen .xcworkspace\n\n# Usage\n## Calculation of differences between two arrays\n### One dimentional array\n#### 1. prepare two arrays.\n```swift\nlet current = [\"Francis\", \"Woodruff\", \"Stanton\"]\nlet next = [\"Francis\", \"Woodruff\", \"Stanton\", \"Eduards\"]\n```\n\n#### 2. calling diff from Array makes EditDistanceProxy\\\u003cT\u003e instance.\n\n```swift\nlet proxy = current.diff // =\u003e EditDistanceProxy\u003cString\u003e\n```\n\n#### 3. the instance has compare(to:) to calculate diff with next array.\n```swift\nlet container = proxy.compare(to: next) // =\u003e EditDistanceContainer\u003cString\u003e\n```\n\n### Two dimentional array\n#### 1. prepare two arrays.\n```swift\nlet current = [[\"Francis\", \"Woodruff\"], [\"Stanton\"]]\nlet next = [[\"Francis\", \"Woodruff\"], [\"Stanton\", \"Eduard\"]]\n```\n\n#### 2. instantiate EditDistance object\n\n```swift\nlet editDistance = EditDistance(from: current, to: next) // =\u003e EditDistance\u003cString\u003e\n```\n\n#### 3. the instance has compare(to:) to calculate diff with next array.\n```swift\nlet container = editDistance.calculate() // =\u003e EditDistanceContainer\u003cString\u003e\n```\n\n### customizing algorithm\n\n#### to preset algorithm objcts\n```swift\nlet container = current.diff.compare(to: next, with: DynamicAlgorithm())\n```\n#### to closure\n```swift\n// implement algorithm\nlet algorithm = AnyEditDistanceAlgorithm { (from, to) -\u003e EditDistanceContainer\u003cString\u003e in\n    //...\n    //...\n}\n\nlet container = current.diff.compare(to: next, with: algorithm)\n```\n#### make a new algorithm class.\n```swift\n//implements protocol\npublic struct Wu\u003cT: Equatable\u003e: EditDistanceAlgorithm {\n    public typealias Element = T\n    \n    public func calculate(from: [[T]], to: [[T]]) -\u003e EditDistanceContainer\u003cT\u003e {\n      //...\n      //...\n    }\n}\n\n```\n\n## Incremental Update to UITableView\n\n### 1. Calculate Diff between two arrays\n```swift\nlet nextDataSource = [\"Francis Elton\", \"Woodruff Chester\", \"Stanton Denholm\", \"Eduard Colby\", \"Farland Ridley\", \"Alex Helton\"]\nlet container = dataSource.diff.compare(to: nextDataSource)\n```\n\n### 2. update DataSource and UI\n```swift\ndataSource = nextDataSource\ntableView.diff.reload(with: container) \n```\n\n## If you won't use this library anymore\n```swift\nataSource = nextDataSource\n// tableView.diff.reload(with: container) \ntableView.reloadData()\n```\n\nThat's it! :wink:\n\n# Performance\nWu's algorithm is recommended in this library. The actual speed depends on the number of differences between two arrays and the cost of \"==\" the elements have. The followings are some avarage speeds for reference. They were executed on iPhone7, iOS 10.2 Simulator and build with \"whole module optimization option\" setting. The sample arrays are composed of random UUID Strings.\n\n- from 100 items to 120 items (20 addition), avg: 0.001 sec\n- from 100 items to 100 items (10 addition and 10 deletion), avg: 0.001 sec\n- from 100 items to 200 items (100 addition), avg: 0.001 ms\n- from 100 items to 100 items (50 addition and 50 deletion), avg: 0.001 sec\n- from 1000 items to 1050 items (50 addition), avg: 0.003 sec\n- from 1000 items to 1000 items (25 addition and 25 deletion), avg: 0.003 sec\n- from 1000 items to 1200 items (200 addition), avg: 0.003 sec\n- from 1000 items to 1000 items (100 addition and 100 deletion), avg: 0.008 sec\n- from 10000 items to 10100 items (100 addition), avg: 0.031 sec\n- from 10000 items to 10000 items (50 addition and 50 deletion), avg: 0.032 sec\n- from 10000 items to 12000 items (2000 addition), avg: 0.033 sec\n- from 10000 items to 10000 items (1000 addition and 1000 deletion), avg: 0.055 sec\n\nTest Case is [here](https://github.com/kazuhiro4949/EditDistance/blob/master/EditDistanceTests/WuTests.swift). You can take reexamination with them.\n\n# Class Design\n\n![editdistance](https://cloud.githubusercontent.com/assets/18320004/23338894/a77d63d4-fc59-11e6-852b-b1036e215eaf.png)\n\n- **EditDistance** is a director to calculate **EditDistanceAlgorithm** with two input Array.\n- **AnyEditDistanceAlgorithm** is a type-erased structure to **EditDistanceAlgorithm**.\n- **EditDistanceContainer** is a container to bridge result of algorithm and view's update.\n- **EditScriptConverter** is a kind of namespace to use some extensions to UIKit classes.\n- **EditScriptConverterProxy** is a proxy for UITableView and UICollectionView. It has method to update the items.\n\n\n# License\n\nCopyright (c) 2017 Kazuhiro Hayashi\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkazuhiro4949%2FEditDistance","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkazuhiro4949%2FEditDistance","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkazuhiro4949%2FEditDistance/lists"}