{"id":19824736,"url":"https://github.com/combinecommunity/combinedatasources","last_synced_at":"2025-04-04T13:09:20.808Z","repository":{"id":41092986,"uuid":"201318649","full_name":"CombineCommunity/CombineDataSources","owner":"CombineCommunity","description":"Table and collection view data sources for Combine","archived":false,"fork":false,"pushed_at":"2023-01-10T14:24:07.000Z","size":1218,"stargazers_count":624,"open_issues_count":7,"forks_count":45,"subscribers_count":18,"default_branch":"master","last_synced_at":"2024-05-22T15:32:38.741Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://combine.community","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/CombineCommunity.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-08-08T18:50:54.000Z","updated_at":"2024-04-22T08:25:47.000Z","dependencies_parsed_at":"2023-02-08T19:00:46.506Z","dependency_job_id":null,"html_url":"https://github.com/CombineCommunity/CombineDataSources","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CombineCommunity%2FCombineDataSources","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CombineCommunity%2FCombineDataSources/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CombineCommunity%2FCombineDataSources/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CombineCommunity%2FCombineDataSources/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/CombineCommunity","download_url":"https://codeload.github.com/CombineCommunity/CombineDataSources/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247182394,"owners_count":20897381,"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":[],"created_at":"2024-11-12T11:05:41.157Z","updated_at":"2025-04-04T13:09:20.789Z","avatar_url":"https://github.com/CombineCommunity.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"![Combine Data Sources](https://github.com/combineopensource/CombineDataSources/raw/master/Assets/combine-data-sources.png)\n\n**CombineDataSources** provides custom Combine subscribers that act as table and collection view controllers and bind a stream of element collections to table or collection sections with cells.  \n\n⚠️⚠️⚠️ **Note** 🚨🚨🚨: The package is currently work in progress.\n\n### Table of Contents\n\n1. [**Usage**](#usage)\n\n1.1 [Bind a plain list of elements](https://github.com/combineopensource/CombineDataSources#bind-a-plain-list-of-elements)\n\n1.2 [Bind a list of Section models](#bind-a-list-of-section-models)\n\n1.2 [Customize the list controller](#customize-the-table-controller)\n\n1.3 [List loaded in batches](#list-loaded-in-batches)\n\n2. [**Installation**](#installation)\n\n2.1 [Swift Package Manager](#swift-package-manager)\n\n2.2 [Cocoapods](#cocoapods)\n\n3. [**License**](#license)\n\n4. [**Credits**](#credits)\n\n---\n\n## Usage\n\n#### Demo App 📱\n\nThe repo contains a demo app in the *Example* sub-folder that demonstrates the different ways to use CombineDataSources in practice.\n\n#### Bind a plain list of elements\n\n```swift\nvar data = PassthroughSubject\u003c[Person], Never\u003e()\n\ndata\n  .bind(subscriber: tableView.rowsSubscriber(cellIdentifier: \"Cell\", cellType: PersonCell.self, cellConfig: { cell, indexPath, model in\n    cell.nameLabel.text = model.name\n  }))\n  .store(in: \u0026subscriptions)\n```\n\n![Plain list updates with CombineDataSources](https://github.com/combineopensource/CombineDataSources/raw/master/Assets/plain-list.gif)\n\nRespectively for a collection view:\n\n```swift\ndata\n  .bind(subscriber: collectionView.itemsSubscriber(cellIdentifier: \"Cell\", cellType: PersonCollectionCell.self, cellConfig: { cell, indexPath, model in\n    cell.nameLabel.text = model.name\n    cell.imageURL = URL(string: \"https://api.adorable.io/avatars/100/\\(model.name)\")!\n  }))\n  .store(in: \u0026subscriptions)\n```\n\n![Plain list updates for a collection view](https://github.com/combineopensource/CombineDataSources/raw/master/Assets/plain-collection.gif)\n\n#### Bind a list of Section models\n\n```swift\nvar data = PassthroughSubject\u003c[Section\u003cPerson\u003e], Never\u003e()\n\ndata\n  .bind(subscriber: tableView.sectionsSubscriber(cellIdentifier: \"Cell\", cellType: PersonCell.self, cellConfig: { cell, indexPath, model in\n    cell.nameLabel.text = model.name\n  }))\n  .store(in: \u0026subscriptions)\n```\n\n![Sectioned list updates with CombineDataSources](https://github.com/combineopensource/CombineDataSources/raw/master/Assets/sections-list.gif)\n\n#### Customize the table controller\n\n```swift\nvar data = PassthroughSubject\u003c[[Person]], Never\u003e()\n\nlet controller = TableViewItemsController\u003c[[Person]]\u003e(cellIdentifier: \"Cell\", cellType: PersonCell.self) { cell, indexPath, person in\n  cell.nameLabel.text = person.name\n}\ncontroller.animated = false\n\n// More custom controller configuration ...\n\ndata\n  .bind(subscriber: tableView.sectionsSubscriber(controller))\n  .store(in: \u0026subscriptions)\n```\n\n#### List loaded in batches\n\nA common pattern for list views is to load a very long list of elements in \"batches\" or \"pages\". (The distinction being that pages imply ordered, equal-length batches.)\n\n**CombineDataSources** includes a data source allowing you to easily implement the batched list pattern called `BatchesDataSource` and a table view controller `TableViewBatchesController` which wraps loading items in batches via the said data source and managing your UI.\n\nIn case you want to implement your own custom logic, you can use directly the data source type:\n\n```swift\nlet input = BatchesInput(\n  reload: resetSubject.eraseToAnyPublisher(),\n  loadNext: loadNextSubject.eraseToAnyPublisher()\n)\n\nlet dataSource = BatchesDataSource\u003cString\u003e(\n  items: [\"Initial Element\"],\n  input: input,\n  initialToken: nil,\n  loadItemsWithToken: { token in\n    return MockAPI.requestBatchCustomToken(token)\n  })\n```\n\n`dataSource` is controlled via the two inputs:\n\n- `input.reload` (to reload the very first batch) and \n\n- `loadNext` (to load each next batch) \n  \n  The data source has four outputs: \n\n- `output.$items` is the current list of elements,\n\n- `output.$isLoading` whether it's currently fetching a batch of elements, \n\n- `output.$isCompleted` whether the data source fetched all available elements, and \n\n- `output.$error` which is a stream of `Error?` elements where errors by the loading closure will bubble up.\n\nIn case you'd like to use the provided controller the code is fairly simple as well. You use the standard table view items controller and `TableViewBatchesController` like so:\n\n```swift\nlet itemsController = TableViewItemsController\u003c[[String]]\u003e(cellIdentifier: \"Cell\", cellType: UITableViewCell.self, cellConfig: { cell, indexPath, text in\n  cell.textLabel!.text = \"\\(indexPath.row+1). \\(text)\"\n})\n\nlet tableController = TableViewBatchesController\u003cString\u003e(\n  tableView: tableView,\n  itemsController: itemsController,\n  initialToken: nil,\n  loadItemsWithToken: { nextToken in\n    MockAPI.requestBatch(token: nextToken)\n  }\n)\n```\n\n`tableController` will set the table view data source, fetch items, and display cells with the proper animations.\n\n## Todo\n\n- [ ] much better README, pls\n- [ ] use a @Published for the time being instead of withLatestFrom\n- [ ] make the batches data source prepend or append the new batch (e.g. new items come from the top or at the bottom)\n- [ ] cover every API with tests\n- [ ] make the default batches view controller neater\n- [ ] add AppKit version of the data sources\n- [x] support Cocoapods\n\n## Installation\n\n### Swift Package Manager\n\nAdd the following dependency to your **Package.swift** file:\n\n```swift\n.package(url: \"https://github.com/combineopensource/CombineDataSources, from: \"0.2\")\n```\n\n### Cocoapods\nAdd the following dependency to your **Podfile**:\n\n```swift\npod 'CombineDataSources'\n```\n\n## License\n\nCombineOpenSource is available under the MIT license. See the LICENSE file for more info.\n\n## Combine Open Source\n\n![Combine Slack channel](Assets/slack.png) \n\nCombineOpenSource Slack channel: [https://combineopensource.slack.com](https://combineopensource.slack.com). \n\n[Sign up here](https://join.slack.com/t/combineopensource/shared_invite/enQtNzQ1MzYyMTMxOTkxLWJkZmNkZDU4MTE4NmU2MjBhYzM5NzI1NTRlNWNhODFiMDEyMjVjOWZmZWI2NmViMzU3ZjZhYjc0YTExOGZmMDM)\n\n## Credits\n\nCreated by Marin Todorov for [CombineOpenSource](https://github.com/combineopensource). \n\n📚 You can support me by checking out our Combine book: [combinebook.com](http://combinebook.com).\n\nInspired by [RxDataSources](https://github.com/RxSwiftCommunity/RxDataSources) and [RxRealmDataSources](https://github.com/RxSwiftCommunity/RxRealmDataSources).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcombinecommunity%2Fcombinedatasources","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcombinecommunity%2Fcombinedatasources","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcombinecommunity%2Fcombinedatasources/lists"}