Skip to content

Instantly share code, notes, and snippets.

@Frankacy
Last active October 15, 2021 10:48
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save Frankacy/519c9e65a135f0831b3d71c8b31e37ee to your computer and use it in GitHub Desktop.
Save Frankacy/519c9e65a135f0831b3d71c8b31e37ee to your computer and use it in GitHub Desktop.
Swift data provider/presenter
//
// DataProviding.swift
// GenericsDataSource
//
// Created by Frank Courville on 2019-05-09.
// Copyright © 2019 iOS Coach Frank. All rights reserved.
//
import UIKit
struct DataProvider<ItemStore, Item> {
var numberOfSections: (ItemStore) -> Int
var numberOfItemsInSection: (ItemStore, Int) -> Int
var itemForIndexPath: (ItemStore, IndexPath) -> Item
var indexPathForItem: (ItemStore, Item) -> IndexPath?
}
extension DataProvider where ItemStore == [Item], Item: Equatable {
static var singleSection: DataProvider {
return DataProvider(
numberOfSections: { _ in return 1 },
numberOfItemsInSection: { data, _ in data.count },
itemForIndexPath: { data, indexPath in data[indexPath.row] },
indexPathForItem: { data, item in
guard let index = data.firstIndex(of: item) else {
return nil
}
return IndexPath(row: 0, section: index)
}
)
}
}
struct TableDataPresenter<Item> {
let registerTableView: (UITableView) -> Void
let cellForRow: (Item, UITableView, IndexPath) -> UITableViewCell
}
struct Fetchable<ItemStore> {
let fetch: ((Result<ItemStore, Error>) -> Void) -> ()
}
class TableViewAdapter<ItemStore, Item>: NSObject, UITableViewDataSource {
var store: ItemStore?
let dataProvider: DataProvider<ItemStore, Item>
let dataPresenter: TableDataPresenter<Item>
let fetchable: Fetchable<ItemStore>
init(dataProvider: DataProvider<ItemStore, Item>, dataPresenter: TableDataPresenter<Item>, data: Input) {
self.fetchable = Fetchable.init(fetch: { completion in completion(.success(data)) })
self.dataProvider = dataProvider
self.dataPresenter = dataPresenter
}
init(dataProvider: DataProvider<ItemStore, Item>, dataPresenter: TableDataPresenter<Item>, fetchable: Fetchable<ItemStore>) {
self.fetchable = fetchable
self.dataProvider = dataProvider
self.dataPresenter = dataPresenter
}
public func loadData(completion: () -> Void) {
fetchable.fetch({ result in
switch result {
case .success(let storage): self.store = store
case .failure(_): break
}
completion()
})
}
public func register(tableView: UITableView) {
dataPresenter.registerTableView(tableView)
}
public func item(atIndexPath indexPath: IndexPath) -> Item? {
guard let store = store else { return nil }
return dataProvider.itemForIndexPath(store, indexPath)
}
public func indexPath(forItem item: Item) -> IndexPath? {
guard let store = store else { return nil }
return dataProvider.indexPathForItem(store, item)
}
func numberOfSections(in tableView: UITableView) -> Int {
guard let store = store else { return 0 }
return dataProvider.numberOfSections(store)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
guard let store = store else { return 0 }
return dataProvider.numberOfItemsInSection(store, section)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let store = store else { fatalError("We don't have any data yet") }
let item = dataProvider.itemForIndexPath(store, indexPath)
let cell = dataPresenter.cellForRow(item, tableView, indexPath)
return cell
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment