// // SettingsDataSource.swift // MullvadVPN // // Created by pronebird on 19/10/2021. // Copyright © 2021 Mullvad VPN AB. All rights reserved. // import MullvadSettings import UIKit final class SettingsDataSource: UITableViewDiffableDataSource, UITableViewDelegate { enum CellReuseIdentifiers: String, CaseIterable { case basicCell var reusableViewClass: AnyClass { SettingsCell.self } } private enum HeaderFooterReuseIdentifier: String, CaseIterable { case spacer var reusableViewClass: AnyClass { EmptyTableViewHeaderFooterView.self } } enum Section: String { case main case version case problemReport } enum Item: String { case preferences case version case problemReport case faq case apiAccess case ipOverride var accessibilityIdentifier: AccessibilityIdentifier { switch self { case .preferences: return .preferencesCell case .version: return .versionCell case .problemReport: return .problemReportCell case .faq: return .faqCell case .apiAccess: return .apiAccessCell case .ipOverride: return .ipOverrideCell } } var reuseIdentifier: CellReuseIdentifiers { .basicCell } } private let interactor: SettingsInteractor private let settingsCellFactory: SettingsCellFactory private var storedAccountData: StoredAccountData? private weak var tableView: UITableView? weak var delegate: SettingsDataSourceDelegate? init(tableView: UITableView, interactor: SettingsInteractor) { self.tableView = tableView self.interactor = interactor let settingsCellFactory = SettingsCellFactory(tableView: tableView, interactor: interactor) self.settingsCellFactory = settingsCellFactory super.init(tableView: tableView) { _, indexPath, itemIdentifier in settingsCellFactory.makeCell(for: itemIdentifier, indexPath: indexPath) } tableView.delegate = self registerClasses() updateDataSnapshot() interactor.didUpdateDeviceState = { [weak self] _ in self?.updateDataSnapshot() } storedAccountData = interactor.deviceState.accountData } // MARK: - UITableViewDelegate func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool { if case .version = itemIdentifier(for: indexPath) { return false } else { return true } } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { guard let item = itemIdentifier(for: indexPath) else { return } delegate?.settingsDataSource(self, didSelectItem: item) } func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { tableView.dequeueReusableHeaderFooterView( withIdentifier: HeaderFooterReuseIdentifier.spacer.rawValue ) } func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { nil } func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return UIMetrics.TableView.sectionSpacing } func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { 0 } // MARK: - Private private func registerClasses() { CellReuseIdentifiers.allCases.forEach { cellIdentifier in tableView?.register( cellIdentifier.reusableViewClass, forCellReuseIdentifier: cellIdentifier.rawValue ) } HeaderFooterReuseIdentifier.allCases.forEach { reuseIdentifier in tableView?.register( reuseIdentifier.reusableViewClass, forHeaderFooterViewReuseIdentifier: reuseIdentifier.rawValue ) } } private func updateDataSnapshot() { var snapshot = NSDiffableDataSourceSnapshot() snapshot.appendSections([.main]) if interactor.deviceState.isLoggedIn { snapshot.appendItems([.preferences], toSection: .main) } snapshot.appendItems([.apiAccess], toSection: .main) snapshot.appendItems([.ipOverride], toSection: .main) snapshot.appendSections([.version, .problemReport]) snapshot.appendItems([.version], toSection: .version) snapshot.appendItems([.problemReport, .faq], toSection: .problemReport) apply(snapshot) } }