A generic small reusable components for data source implementation for UITableView/UICollectionView in Swift.

Overview

GenericDataSource

Swift Version

Version

Build Status Coverage Status Documentation

A generic small reusable components for data source implementation for UITableView/UICollectionView written in Swift.

Features

  • BasicDataSource easily bind model to cells with automatic dequeuing.
  • SegmentedDataSource easily build segmented controls or for empty state of your UICollectionView/UITableView data source.
  • CompositeDataSource builds complex cells/models structure with easy to use components (BasicDataSource SegmentedDataSource or other CompositeDataSource).
  • UICollectionView supplementary, UITableView header, and footer views support.
  • Ability to override any data source method from UIKit classes.
  • Comprehensive Unit Test Coverage.
  • Complete Documentation

Requirements

  • iOS 8.0+
  • Xcode 10
  • Swift 4.0+

Installation

CocoaPods

To integrate GenericDataSource into your Xcode project using CocoaPods, specify it in your Podfile:

pod 'GenericDataSources'

IMPORTANT: The pod name is GenericDataSources with "s" at the end.

Carthage

To integrate GenericDataSource into your Xcode project using Carthage, specify it in your Cartfile:

github "GenericDataSource/GenericDataSource"

Manually

Add GenericDataSource.xcodeproj to your project file by drag and drop.

You can then consult to Adding an Existing Framework to a Project.


Example

Basic Data Source Example

UITableView

Create a basic data source and bind it to to a table view.

let dataSource = BasicBlockDataSource<Example, BasicTableViewCell>() { (item: Example, cell: BasicTableViewCell, indexPath) -> Void in
    cell.titleLabel?.text = item.title
}

// Need to keep a strong reference to our data source.
self.dataSource = dataSource

// register the cell
tableView.ds_register(cellClass: BasicTableViewCell.self)
// bind the data source to the table view
tableView.ds_useDataSource(dataSource)

dataSource.items =  <<retrieve items>> // Can be set and altered at anytime

That's it! Your first data source is implemented. No dequeuing! no casting! simple and smart.

UICollectionView

Let's now take it to the next level. Suppose after we implemented it, the requirements changed and we need to implement it using UICollectionView.

let dataSource = BasicBlockDataSource<Example, BasicCollectionViewCell>() { (item: Example, cell: BasicCollectionViewCell, indexPath) -> Void in
    cell.titleLabel?.text = item.title
}

// Need to keep a strong reference to our data source.
self.dataSource = dataSource

// register the cell
collectionView.ds_register(cellClass: BasicCollectionViewCell.self)
// bind the data source to the collection view
collectionView.ds_useDataSource(dataSource)

dataSource.items =  <<retrieve items>> // Can be set and altered at anytime

All you need to do is change the cell class and of course the table view to collection view.

Actually this opens the door for so much possibilities. You can inherit from BasicDataSource and implement your custom generic data source that is based on a protocol implemented by the cell and you don't need to repeat the configuration part. You would create data source like that.

let dataSource1 = CustomDataSource<BasicTableViewCell>() // for table view
let dataSource2 = CustomDataSource<BasicCollectionViewCell>() // for collection view

App store Featured Example

Suppose we want to implement the following screen, the App Store featured tab.

App Store Example Screenshot

If you want to have a look at the complete source code, it is under Example project -> AppStoreViewController.swift.

  1. We will create cells as we do normally.
  2. Now we need to think about DataSources.
  3. It's simple, one data source for each cell type (BasicDataSource).
  4. CompositeDataSource(sectionType: .single) for the table view rows. Since these rows are of different cell types.
  5. SegmentedDataSource for switching between loading and data views.
  6. Bind the SegmentedDataSource data source to the table view and that's it.
  7. See how we think structurally about our UI and data sources instead of one big cell.

One thing we didn't talk about is the UICollectionView of the featured section cells. It's very simple, just BasicDataSource.

See how we can implement the screen in the following code:

  1. Create the cells.
class AppStoreFeaturedSectionTableViewCell: UITableViewCell { ... }
class AppStoreQuickLinkLabelTableViewCell: UITableViewCell { ... }
class AppStoreQuickLinkTableViewCell: UITableViewCell { ... }
class AppStoreFooterTableViewCell: UITableViewCell { ... }
class AppStoreLoadingTableViewCell: UITableViewCell { ... }
  1. Create BasicDataSources.
class AppStoreLoadingDataSource: BasicDataSource<Void, AppStoreLoadingTableViewCell> {
    // loading should take full screen size.
    override func ds_collectionView(_ collectionView: GeneralCollectionView, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return collectionView.size
    }
}
class AppStoreFooterDataSource: BasicDataSource<Void, AppStoreFooterTableViewCell> { ... }
class AppStoreQuickLinkDataSource: BasicDataSource<FeaturedQuickLink, AppStoreQuickLinkTableViewCell> { ...  }
class AppStoreFeaturedAppsDataSource: BasicDataSource<FeaturedApp, AppStoreFeaturedAppCollectionViewCell> { ... }
class AppStoreFeaturedAppsSectionDataSource: BasicDataSource<FeaturedSection, AppStoreFeaturedSectionTableViewCell> { ... }
class AppStoreQuickLinkLabelDataSource: BasicDataSource<String, AppStoreQuickLinkLabelTableViewCell> { ... }
  1. Create CompositeDataSource that holds the featured page.
class AppStoreFeaturedPageDataSource: CompositeDataSource {
    init() { super.init(sectionType: .single)] }

    var page: FeaturedPage? {
        didSet {
            // remove all existing data sources
            removeAllDataSources()

            guard let page = page else {
                return
            }

            // add featured apps
            let featuredApps = AppStoreFeaturedAppsSectionDataSource()
            featuredApps.setSelectionHandler(UnselectableSelectionHandler())
            featuredApps.items = page.sections
            add(featuredApps)

            // add quick link label
            let quickLinkLabel = AppStoreQuickLinkLabelDataSource()
            quickLinkLabel.setSelectionHandler(UnselectableSelectionHandler())
            quickLinkLabel.items = [page.quickLinkLabel]
            add(quickLinkLabel)

            // add quick links
            let quickLinks = AppStoreQuickLinkDataSource()
            quickLinks.items = page.quickLinks
            add(quickLinks)

            // add footer
            let footer = AppStoreFooterDataSource()
            footer.setSelectionHandler(UnselectableSelectionHandler())
            footer.items = [Void()] // we add 1 element to show the footer, 2 elements will show it twice. 0 will not show it.
            add(footer)
        }
    }
}
  1. Create the outer most data source.
class AppStoreDataSource: SegmentedDataSource {

    let loading = AppStoreLoadingDataSource()
    let page = AppStoreFeaturedPageDataSource()

    // reload data on index change
    override var selectedDataSourceIndex: Int {
        didSet {
            ds_reusableViewDelegate?.ds_reloadData()
        }
    }

    override init() {
        super.init()
        loading.items = [Void()] // we add 1 element to show the loading, 2 elements will show it twice. 0 will not show it.

        add(loading)
        add(page)
    }
}
  1. Register cells.
tableView.ds_register(cellNib: AppStoreFeaturedSectionTableViewCell.self)
tableView.ds_register(cellNib: AppStoreQuickLinkLabelTableViewCell.self)
tableView.ds_register(cellNib: AppStoreQuickLinkTableViewCell.self)
tableView.ds_register(cellNib: AppStoreFooterTableViewCell.self)
tableView.ds_register(cellNib: AppStoreLoadingTableViewCell.self)
  1. Set data sources to the collection view.
tableView.ds_useDataSource(dataSource)
  1. Finally set the data when it is available.
  // show loading indicator
  dataSource.selectedDataSourceIndex = 0

  // get the data from the service
  service.getFeaturedPage { [weak self] page in

    // update the data source model
    self?.dataSource.page.page = page

    // show the page
    self?.dataSource.selectedDataSourceIndex = 1
}

There are many benefits of doing that:

  1. Lightweight view controllers.
  2. You don't need to think about indexes anymore, all is handled for us. Only think about how you can structure your cells into smaller data sources.
  3. We can switch between UITableView and UICollectionView without touching data sources or models. Only change the cells to inherit from UITableViewCell or UICollectionViewCell and everything else works.
  4. We can add/delete/update cells easily. For example we decided to add more blue links. We can do it by just adding new item to the array passed to the data source.
  5. We can re-arrange cells as we want. Just move around the add of data sources calls.
  6. Most importantly no if/else in our code.

Check the Examples application for complete implementations.

Attribution

The main idea comes from [WWDC 2014 Advanced User Interfaces with Collection Views] (https://developer.apple.com/videos/play/wwdc2014/232/) written in swift with generics.

Author

Mohamed Afifi, [email protected]

License

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

Comments
  • Future of project

    Future of project

    Hey @mohamede1945 this looks like a great project.

    Do you have plans to actively update/maintain this project going forward?

    I'm wondering about support for section titles, placeholders, activity indicator, children refreshing parents, etc. Making it more on par with the Apple Example code.

    opened by ryanjm 4
  • Pre-select a row?

    Pre-select a row?

    Are there any hooks that can be used to pre-select a row when the table first gets loaded? I'd normally just have a method that is called on viewDidLoad which selects the cells, but since that information is now spread over a couple data sources, I'm not sure what the right way is to do that.

    Do I still need to write that method and then have it call each data source? Also, should batch updates be used in this case?

    Still trying to get a feel for the project as a whole. 😄

    opened by ryanjm 3
  • Support HeaderView/ FooterView for Sections

    Support HeaderView/ FooterView for Sections

    How to add footer or header view for each section.

    in Objective C we can use: - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {}

    opened by faris-mobile89 2
  • Tableview cell height

    Tableview cell height

    I might be skipping over some things but how would I resize the height of each table view cell? I'm currently just doing tableView.rowHeight = whatever.

    opened by kennyDang 1
  • How to add Item Selected event in the AppStore example

    How to add Item Selected event in the AppStore example

    Hello,

    I love your library, and I'm trying to use it for my app. I'm using the AppStore example as a starter, and I'm trying to add the similar selection handler you have for the contact/color example (I need to navigate to another VC when a cell is selected) But somehow I'm struggling to do that, I'm lost with all those protocol and template issue. Will you be able to guide me on how best doing that ? Thanks ! Vince

    opened by aerobace 1
  • Inserting a new cell animation

    Inserting a new cell animation

    I am testing some code:

     @objc private func addItem() {
            items.append("")
            datasource.items = items
            let index = items.endIndex - 1
            let indexPath = IndexPath(item: index, section: 0)
            datasource.ds_reusableViewDelegate?.ds_performBatchUpdates({
                self.datasource.ds_reusableViewDelegate?.ds_insertItems(at: [indexPath], with: .fade)
            }, completion: nil)
        }
    

    I got this working so whenever I tap on my view, this method gets called. It appends a new piece of data to items and then I update the data source with it. Then I do an animation to insert each individual new cell. Is there a better/cleaner way of doing this?

    Edit: formatting

    opened by kennyDang 3
  • Remove unneeded

    Remove unneeded ")" at the end of the description

    let propertiesDescription = properties.filter { $1 != nil }.map { "\($0)=\($1!))" }.joined(separator: " ;")
    

    should be

    let propertiesDescription = properties.filter { $1 != nil }.map { "\($0)=\($1!)" }.joined(separator: " ;")
    
    opened by mohamede1945 0
  • How to achieve section insets

    How to achieve section insets

    Hey,

    as far as i investigated the code base there is no way to support func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets

    Do you have any idea how i could make use of that method when using a BasicBlockDataSource?

    thanks

    opened by davidkraus 1
Releases(3.0.3)
  • 3.0.3(Sep 23, 2018)

  • 3.0.2(Apr 24, 2018)

  • 3.0.1(Oct 30, 2017)

  • 3.0.0(Oct 30, 2017)

  • 2.4.5(Apr 20, 2017)

    • Adding onItemsUpdated to basic data sources to monitor changes to items property.
    • Adding most non-essential APIs to be DataSourceSelector so that, it's delegate methods is not called by default.
    Source code(tar.gz)
    Source code(zip)
  • 2.4.4(Apr 20, 2017)

    • Added the ability for CompositeDataSource and SegmentedDataSource to automatically ds_responds(to selector: DataSourceSelector) if the subclass implemented the selector.
    Source code(tar.gz)
    Source code(zip)
  • 2.4.3(Apr 20, 2017)

    • Fix registering header/footer class method name to be func ds_register(headerFooterClass view: UITableViewHeaderFooterView.Type) instead of incorrect old name func ds_register(headerFooterNib view: UITableViewHeaderFooterView.Type).
    Source code(tar.gz)
    Source code(zip)
  • 2.4.2(Apr 5, 2017)

    • Adding asCollectionView() and asTableView() methods to GeneralCollectionView to convert it to UICollectionView and UITableView respectively.
    • Adding size property to GeneralCollectionView to get the size of the underlying UICollectionView/UITableView.
    Source code(tar.gz)
    Source code(zip)
  • 2.4.1(Mar 31, 2017)

  • 2.4.0(Mar 30, 2017)

    • ds_shouldConsumeItemSizeDelegateCalls is unavailable, instead use ds_responds(to selector: DataSourceSelector) -> Bool, It takes an enum, with .size it act the same as ds_shouldConsumeItemSizeDelegateCalls.
    • Fixes a bug that makes all table view cells editable by default.
    • New ds_responds(to selector: DataSourceSelector) -> Bool to make it so easy to make some implementations of DataSource methods optional (e.g. we used it to fix the editable table view cells bug).
    Source code(tar.gz)
    Source code(zip)
  • 2.3.1(Mar 30, 2017)

  • 2.3.0(Mar 23, 2017)

  • 2.2.1(Mar 4, 2017)

    • Making Supplementary view optional as a workaround for the UITableView with .grouped style as it asks for the header/footer view even if the size is set as 0.
    Source code(tar.gz)
    Source code(zip)
  • 2.2.0(Mar 4, 2017)

  • 2.1.0(Oct 12, 2016)

  • 2.0.0(Oct 12, 2016)

  • 1.0.1(Aug 1, 2016)

    • Deprecating useDelegateForItemSize in favor of automatic detection if the user implemented ds_collectionView(_:sizeForItemAtIndexPath:) or not.
    • Adding more code documentation and enhancing the readme file.
    Source code(tar.gz)
    Source code(zip)
Owner
null
An iOS drop-in UITableView, UICollectionView and UIScrollView superclass category for showing a customizable floating button on top of it.

MEVFloatingButton An iOS drop-in UITableView, UICollectionView, UIScrollView superclass category for showing a customizable floating button on top of

Manuel Escrig 298 Jul 17, 2022
Automates prefetching of content in UITableView and UICollectionView

Automates preheating (prefetching) of content in UITableView and UICollectionView. Deprecated on iOS 10. This library is similar to UITableViewDataSou

Alexander Grebenyuk 633 Sep 16, 2022
Incremental update tool to UITableView and UICollectionView

EditDistance is one of the incremental update tool for UITableView and UICollectionView. The followings show how this library update UI. They generate

Kazuhiro Hayashi 90 Jun 9, 2022
🚴 A declarative library for building component-based user interfaces in UITableView and UICollectionView.

A declarative library for building component-based user interfaces in UITableView and UICollectionView. Declarative Component-Based Non-Destructive Pr

Ryo Aoyama 1.2k Jan 5, 2023
ZHTCView - UITableview & UICollectionView

ZHTCView 这是一个使用Block替换代理的UITableview & UICollectionView。 使用方法如下: - (DSTableView *)tableView { if (!_tableView) { _tableView = DSTableView.

黑酒一 0 Jan 10, 2022
Generic collection view controller with external data processing

FlexibleCollectionViewController Swift library of generic collection view controller with external data processing of functionality, like determine ce

Dmytro Pylypenko 3 Jul 16, 2018
Reusable iOS's behavior drag or swipe to pop ViewController

DLSwipeToPopController Reusable iOS's behavior to pop ViewController base on SwipeRightToPopController: Swipe from Right to Left to pop ViewController

Le Ngoc Duy 1 Sep 17, 2022
A data-driven UICollectionView framework for building fast and flexible lists.

A data-driven UICollectionView framework for building fast and flexible lists. Main Features ?? Never call performBatchUpdates(_:, completion:) or rel

Instagram 12.5k Jan 1, 2023
Conv smart represent UICollectionView data structure more than UIKit.

Conv Conv smart represent UICollectionView data structure more than UIKit. Easy definition for UICollectionView DataSource and Delegate methods. And C

bannzai 157 Nov 25, 2022
Conv smart represent UICollectionView data structure more than UIKit.

Conv Conv smart represent UICollectionView data structure more than UIKit. Easy definition for UICollectionView DataSource and Delegate methods. And C

bannzai 155 May 12, 2022
PJFDataSource is a small library that provides a simple, clean architecture for your app to manage its data sources while providing a consistent user interface for common content states (i.e. loading, loaded, empty, and error).

PJFDataSource PJFDataSource is a small library that provides a simple, clean architecture for your app to manage its data sources while providing a co

Square 88 Jun 30, 2022
💾 A library for backporting UITableView/UICollectionViewDiffableDataSource.

DiffableDataSources ?? A library for backporting UITableView/UICollectionViewDiffableDataSource powered by DifferenceKit. Made with ❤️ by Ryo Aoyama I

Ryo Aoyama 762 Dec 28, 2022
Easier way to represent the structure of UITableView.

Shoyu Shoyu is a library written in Swift to represent UITableView data structures. Shoyu means Soy Sauce in Japanese. Usage Create single section and

yukiasai 278 Apr 14, 2022
UICollectionView layout for presenting of the overlapping cells.

StickyCollectionView UICollectionView layout for presenting of the overlapping cells. Objective-C version here Checkout demo Overview Installation Man

Bogdan Matveev 325 Oct 11, 2022
Reimagining UICollectionView

CollectionKit Reimagining UICollectionView A modern Swift framework for building composable data-driven collection view. Migration Guide v2.0 Features

SoySauceLab 4.3k Dec 27, 2022
Collapse and expand UICollectionView sections with one method call.

This library provides a custom UICollectionView that allows to expand and collapse sections. Provides a simple API to manage collection view appearanc

Touchlane 172 Dec 26, 2022
CollectionView - UICollectionView using UICollectionViewCompositionalLayout

CollectionView UICollectionView using UICollectionViewCompositionalLayout create

null 0 Jan 11, 2022
CollectionViewSegmentedControl - Scrollable UISegmentedControl built using a UICollectionView

CollectionViewSegmentedControl Installation CocoaPods Download CocoaPods Run 'Po

James Sedlacek 7 Nov 24, 2022
Protocol-oriented UICollectionView management, powered by generics and associated types.

DTCollectionViewManager Features Powerful mapping system between data models and cells, headers and footers Automatic datasource and interface synchro

Denys Telezhkin 308 Jan 6, 2023