Home

Awesome

<p align="center"> <img width=656 src="logo.png"> </p>

DataSources

💾 🔜📱 Type-safe data-driven List-UI Framework. (We can also use ASCollectionNode)

Partial updates(insert, delete, move) of UICollectionView/UITableView is important things for fancy UI.<br> But, It's hard that synchronous of data and UI.<br> DataSources will solve this problem.

CI Status Version License Platform Carthage compatible FOSSA Status

Thanks

Diff-algorithm

Features

Requirements

Usage (Example)

Conform protocol Diffable

public protocol Diffable {
  associatedtype Identifier : Hashable
  var diffIdentifier: Identifier { get }
}
struct Model : Diffable {

  var diffIdentifier: String {
    return id
  }
  
  let id: String
}

🤠 Most Simplified Usage

  1. Define SectionDataController in ViewController
let collectionView: UICollectionView

let sectionDataController = SectionDataController<Model, CollectionViewAdapter>(
  adapter: CollectionViewAdapter(collectionView: self.collectionView),
  isEqual: { $0.id == $1.id } // If Model has Equatable, you can omit this closure.
)

var models: [Model] = [] {
  didSet {
    sectionDataController.update(items: items, updateMode: .partial(animated: true), completion: {
      // Completed update
    })
  }
}

let dataSource = CollectionViewDataSource(sectionDataController: sectionDataController)

dataSource.cellFactory = { _, collectionView, indexPath, model in
   let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! Cell
   cell.label.text = model.title
   return cell
 }

collectionView.dataSource = dataSource

😎 Semi Manual

Single-Section (UICollectionView)

  1. Define SectionDataController in ViewController
let collectionView: UICollectionView
var models: [Model]

let sectionDataController = SectionDataController<Model, CollectionViewAdapter>(
  adapter: CollectionViewAdapter(collectionView: self.collectionView),
  isEqual: { $0.id == $1.id } // If Model has Equatable, you can omit this closure.
)
  1. Bind Models to SectionDataController in ViewController
var models: [Model] = […] {
  didSet {
    sectionDataController.update(items: items, updateMode: .partial(animated: true), completion: {
      // Completed update
    })
  }
}
  1. Implement UICollectionViewDataSource in ViewController
func numberOfSections(in collectionView: UICollectionView) -> Int {
  return 1
}

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
  return sectionDataController.numberOfItems()
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

  let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! Cell
  let m = sectionDataController.item(for: indexPath)
  cell.label.text = m.title
  return cell
}

Multiple-Section (UICollectionView)

  1. Define DataController in ViewController
let collectionView: UICollectionView
var models: [Model]

let dataController = DataController<CollectionViewAdapter>(adapter: CollectionViewAdapter(collectionView: self.collectionView))
  1. Define Section<T> in ViewController
let section0 = Section(ModelA.self, isEqual: { $0.id == $1.id })
let section1 = Section(ModelB.self, isEqual: { $0.id == $1.id })
  1. Add Section to DataController

Order of Section will be decided in the order of addition.

dataController.add(section: section0) // will be 0 of section
dataController.add(section: section1) // will be 1 of section
  1. Bind Models to DataController
var section0Models: [ModelA] = […] {
  didSet {
    dataController.update(
      in: section0,
      items: section0Models,
      updateMode: .partial(animated: true),
      completion: {
        
    })
  }
}

var section1Models: [ModelA] = […] {
  didSet {
    dataController.update(
      in: section1,
      items: section1Models,
      updateMode: .partial(animated: true),
      completion: {
        
    })
  }
}
  1. Implement UICollectionViewDataSource
func numberOfSections(in collectionView: UICollectionView) -> Int {
  return dataController.numberOfSections()
}

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
  return dataController.numberOfItems(in: section)
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

  return dataController.item(
    at: indexPath,    
    handlers: [
    .init(section: section0) { (m: ModelA) in
      let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! Cell
      cell.label.text = m.title
      return cell
    },
    .init(section: section1) { (m: ModelB) in
      let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! Cell
      cell.label.text = m.title
      return cell
      },
    ])

  /* Other way
  switch indexPath.section {
  case section0:
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! Cell
    let m = _dataController.item(at: indexPath, in: section0)
    cell.label.text = m.title
    return cell
  case section1:
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! Cell
    let m = _dataController.item(at: indexPath, in: section1)
    cell.label.text = m.title
    return cell
  default:
    fatalError()
  }
   */
}

Reorder by UI operation

SectionDataController has a snapshot for List-UI. It helps that perform batch update List-UI in safety.

But, the snapshots include side-effects. For example, if we did reorder items of List-UI by UI operation. In this time, Items of List-UI is caused differences to the snapshot. It will be caused unnecessary diff.

Therefore when we reorder items, we should operation followings.

  1. Reorder items of UI
  2. Call SectionDataController.reserveMoved(...
  3. Reorder items of Array
  4. Call SectionDataController.update(items: [T]..

Demo Application

This repository include Demo-Application. You can touch DataSources.

  1. Clone repository.
$ git clone https://github.com/muukii/DataSources.git
$ cd DataSources
$ pod install
  1. Open xcworkspace
  2. Run DataSourcesDemo on iPhone Simulator.

Author

muukii, m@muukii.me, https://muukii.me/

License

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

FOSSA Status