// // ConnectionsTableViewController.swift // WireGuard // // Created by Jeroen Leenarts on 23-05-18. // Copyright © 2018 WireGuard. All rights reserved. // import UIKit import CoreData import BNRCoreDataStack protocol ConnectionsTableViewControllerDelegate: class { func addProvider(connectionsTableViewController: ConnectionsTableViewController) func connect(profile: Profile, connectionsTableViewController: ConnectionsTableViewController) func configure(profile: Profile, connectionsTableViewController: ConnectionsTableViewController) func delete(profile: Profile, connectionsTableViewController: ConnectionsTableViewController) } class ConnectionsTableViewController: UITableViewController { weak var delegate: ConnectionsTableViewControllerDelegate? var viewContext: NSManagedObjectContext! private lazy var fetchedResultsController: FetchedResultsController = { let fetchRequest = NSFetchRequest() fetchRequest.entity = Profile.entity() fetchRequest.sortDescriptors = [NSSortDescriptor(key: "title", ascending: true)] let frc = FetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: viewContext) frc.setDelegate(self.frcDelegate) return frc }() private lazy var frcDelegate: ProfileFetchedResultsControllerDelegate = { // swiftlint:disable:this weak_delegate return ProfileFetchedResultsControllerDelegate(tableView: self.tableView) }() override func viewDidLoad() { super.viewDidLoad() do { try fetchedResultsController.performFetch() } catch { print("Failed to fetch objects: \(error)") } } @IBAction func addProvider(_ sender: Any) { delegate?.addProvider(connectionsTableViewController: self) } override func numberOfSections(in tableView: UITableView) -> Int { return 1 } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return fetchedResultsController.sections?[0].objects.count ?? 0 } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(type: ProfileTableViewCell.self, for: indexPath) guard let sections = fetchedResultsController.sections else { fatalError("FetchedResultsController \(fetchedResultsController) should have sections, but found nil") } let section = sections[indexPath.section] let profile = section.objects[indexPath.row] cell.textLabel?.text = profile.title return cell } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { guard let sections = fetchedResultsController.sections else { fatalError("FetchedResultsController \(fetchedResultsController) should have sections, but found nil") } let section = sections[indexPath.section] let profile = section.objects[indexPath.row] delegate?.connect(profile: profile, connectionsTableViewController: self) tableView.deselectRow(at: indexPath, animated: true) } override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) { guard let sections = fetchedResultsController.sections else { fatalError("FetchedResultsController \(fetchedResultsController) should have sections, but found nil") } let section = sections[indexPath.section] let profile = section.objects[indexPath.row] delegate?.configure(profile: profile, connectionsTableViewController: self) } override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { return true } override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) { if editingStyle == .delete { guard let sections = fetchedResultsController.sections else { fatalError("FetchedResultsController \(fetchedResultsController) should have sections, but found nil") } let section = sections[indexPath.section] let profile = section.objects[indexPath.row] delegate?.delete(profile: profile, connectionsTableViewController: self) } } } extension ConnectionsTableViewController: Identifyable {} class ProfileFetchedResultsControllerDelegate: NSObject, FetchedResultsControllerDelegate { private weak var tableView: UITableView? // MARK: - Lifecycle init(tableView: UITableView) { self.tableView = tableView } func fetchedResultsControllerDidPerformFetch(_ controller: FetchedResultsController) { tableView?.reloadData() } func fetchedResultsControllerWillChangeContent(_ controller: FetchedResultsController) { tableView?.beginUpdates() } func fetchedResultsControllerDidChangeContent(_ controller: FetchedResultsController) { tableView?.endUpdates() } func fetchedResultsController(_ controller: FetchedResultsController, didChangeObject change: FetchedResultsObjectChange) { guard let tableView = tableView else { return } switch change { case let .insert(_, indexPath): tableView.insertRows(at: [indexPath], with: .automatic) case let .delete(_, indexPath): tableView.deleteRows(at: [indexPath], with: .automatic) case let .move(_, fromIndexPath, toIndexPath): tableView.moveRow(at: fromIndexPath, to: toIndexPath) case let .update(_, indexPath): tableView.reloadRows(at: [indexPath], with: .automatic) } } func fetchedResultsController(_ controller: FetchedResultsController, didChangeSection change: FetchedResultsSectionChange) { guard let tableView = tableView else { return } switch change { case let .insert(_, index): tableView.insertSections(IndexSet(integer: index), with: .automatic) case let .delete(_, index): tableView.deleteSections(IndexSet(integer: index), with: .automatic) } } } class ProfileTableViewCell: UITableViewCell { } extension ProfileTableViewCell: Identifyable {}