Need help with CombineDataSources?
Click the “chat” button below for chat support from the developer who created it, or find similar developers for support.

About the developer

CombineCommunity
455 Stars 29 Forks MIT License 50 Commits 4 Opened issues

Description

Table and collection view data sources for Combine

Services available

!
?

Need anything else?

Contributors list

# 13,644
Swift
Objecti...
realm
realmsw...
33 commits
# 251,261
Swift
iOS
Shell
Objecti...
6 commits
# 645,342
Swift
Ruby
1 commit

Combine Data Sources

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.

⚠️⚠️⚠️ Note 🚨🚨🚨: The package is currently work in progress.

Table of Contents

  1. Usage

1.1 Bind a plain list of elements

1.2 Bind a list of Section models

1.2 Customize the list controller

1.3 List loaded in batches

  1. Installation

2.1 Swift Package Manager

2.2 Cocoapods

  1. License

  2. Credits


Usage

Demo App 📱

The repo contains a demo app in the Example sub-folder that demonstrates the different ways to use CombineDataSources in practice.

Bind a plain list of elements

var data = PassthroughSubject()

data .bind(subscriber: tableView.rowsSubscriber(cellIdentifier: "Cell", cellType: PersonCell.self, cellConfig: { cell, indexPath, model in cell.nameLabel.text = model.name })) .store(in: &subscriptions)

Plain list updates with CombineDataSources

Respectively for a collection view:

data
  .bind(subscriber: collectionView.itemsSubscriber(cellIdentifier: "Cell", cellType: PersonCollectionCell.self, cellConfig: { cell, indexPath, model in
    cell.nameLabel.text = model.name
    cell.imageURL = URL(string: "https://api.adorable.io/avatars/100/\(model.name)")!
  }))
  .store(in: &subscriptions)

Plain list updates for a collection view

Bind a list of Section models

var data = PassthroughSubject], Never>()

data .bind(subscriber: tableView.sectionsSubscriber(cellIdentifier: "Cell", cellType: PersonCell.self, cellConfig: { cell, indexPath, model in cell.nameLabel.text = model.name })) .store(in: &subscriptions)

Sectioned list updates with CombineDataSources

Customize the table controller

var data = PassthroughSubject()

let controller = TableViewItemsController(cellIdentifier: "Cell", cellType: PersonCell.self) { cell, indexPath, person in cell.nameLabel.text = person.name } controller.animated = false

// More custom controller configuration ...

data .bind(subscriber: tableView.sectionsSubscriber(controller)) .store(in: &subscriptions)

List loaded in batches

A 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.)

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.

In case you want to implement your own custom logic, you can use directly the data source type:

let input = BatchesInput(
  reload: resetSubject.eraseToAnyPublisher(),
  loadNext: loadNextSubject.eraseToAnyPublisher()
)

let dataSource = BatchesDataSource( items: ["Initial Element"], input: input, initialToken: nil, loadItemsWithToken: { token in return MockAPI.requestBatchCustomToken(token) })

dataSource
is controlled via the two inputs:
  • input.reload
    (to reload the very first batch) and
  • loadNext
    (to load each next batch)

The data source has four outputs:

  • output.$items
    is the current list of elements,
  • output.$isLoading
    whether it's currently fetching a batch of elements,
  • output.$isCompleted
    whether the data source fetched all available elements, and
  • output.$error
    which is a stream of
    Error?
    elements where errors by the loading closure will bubble up.

In 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:
let itemsController = TableViewItemsController(cellIdentifier: "Cell", cellType: UITableViewCell.self, cellConfig: { cell, indexPath, text in
  cell.textLabel!.text = "\(indexPath.row+1). \(text)"
})

let tableController = TableViewBatchesController( tableView: tableView, itemsController: itemsController, initialToken: nil, loadItemsWithToken: { nextToken in MockAPI.requestBatch(token: nextToken) } )

tableController
will set the table view data source, fetch items, and display cells with the proper animations.

Todo

  • [ ] much better README, pls
  • [ ] use a @Published for the time being instead of withLatestFrom
  • [ ] make the batches data source prepend or append the new batch (e.g. new items come from the top or at the bottom)
  • [ ] cover every API with tests
  • [ ] make the default batches view controller neater
  • [ ] add AppKit version of the data sources
  • [x] support Cocoapods

Installation

Swift Package Manager

Add the following dependency to your Package.swift file:

.package(url: "https://github.com/combineopensource/CombineDataSources, from: "0.2")

Cocoapods

Add the following dependency to your Podfile:

pod 'CombineDataSources'

License

CombineOpenSource is available under the MIT license. See the LICENSE file for more info.

Combine Open Source

Combine Slack channel

CombineOpenSource Slack channel: https://combineopensource.slack.com.

Sign up here

Credits

Created by Marin Todorov for CombineOpenSource.

📚 You can support me by checking out our Combine book: combinebook.com.

Inspired by RxDataSources and RxRealmDataSources.

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.