Last active
October 15, 2021 10:48
-
-
Save Frankacy/519c9e65a135f0831b3d71c8b31e37ee to your computer and use it in GitHub Desktop.
Swift data provider/presenter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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