From 0816c62da7418c24ff1852342ccc0d4d41755c6b Mon Sep 17 00:00:00 2001 From: Alexander Koulabukhov Date: Mon, 1 Jul 2019 22:30:41 +0300 Subject: [PATCH] Add possibility to implement custom LiveList filters --- Example/Percy/ViewController.swift | 2 +- Percy/Classes/Percy+LiveList.swift | 56 ++++++++++++++++++++++++++---- 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/Example/Percy/ViewController.swift b/Example/Percy/ViewController.swift index 690acc7..992dcf6 100644 --- a/Example/Percy/ViewController.swift +++ b/Example/Percy/ViewController.swift @@ -26,7 +26,7 @@ class ViewController: UIViewController { func setupLiveList() { // Filter only correct emails, sort by ID ASC - liveList = percy.makeLiveList(filter: NSPredicate(format:"email LIKE[cd] %@", "*@*.??"), sorting: { $0.id < $1.id }) + liveList = percy.makeLiveList(predicate: NSPredicate(format:"email LIKE[cd] %@", "*@*.??"), sorting: { $0.id < $1.id }) liveList?.onChange = { [unowned self] in $0.updateTableView(self.tableView) } liveList?.onFinish = { [unowned self] in self.refreshFooter() } } diff --git a/Percy/Classes/Percy+LiveList.swift b/Percy/Classes/Percy+LiveList.swift index b4529ad..a489b07 100644 --- a/Percy/Classes/Percy+LiveList.swift +++ b/Percy/Classes/Percy+LiveList.swift @@ -9,11 +9,20 @@ import Foundation import CoreData extension Percy { - public func makeLiveList(filter: NSPredicate? = nil, sorting: LiveList.Sorting? = nil) -> LiveList { + public func makeLiveList(predicate: NSPredicate? = nil, sorting: LiveList.Sorting? = nil) -> LiveList { + return LiveList(context: mainContext, filter: predicate.flatMap { PredicateFilter(predicate: $0) }, sorting: sorting, in: self) + } + public func makeLiveList(filter: F, sorting: LiveList.Sorting? = nil) -> LiveList where F.Entity == T { return LiveList(context: mainContext, filter: filter, sorting: sorting, in: self) } } +public protocol LiveListFilter { + associatedtype Entity: Persistable + func getFilteredEntities(from percy: Percy) -> [Entity] + func evaluate(with entity: Entity, sourceObject object: NSManagedObject) -> Bool +} + public final class LiveList { public typealias Sorting = (T, T) -> Bool @@ -25,7 +34,8 @@ public final class LiveList { } private unowned let percy: Percy - private let filter: NSPredicate? + private let fetcher: ((Percy) -> [T])? + private let filter: ((T, NSManagedObject) -> Bool)? private let sorting: Sorting? public private(set) var items = [T]() @@ -34,8 +44,9 @@ public final class LiveList { public var onChange: ((Change) -> Void)? public var onFinish: (() -> Void)? - init(context: NSManagedObjectContext, filter: NSPredicate?, sorting: Sorting?, in percy: Percy) { - self.filter = filter + init(context: NSManagedObjectContext, filter: Filter?, sorting: Sorting?, in percy: Percy) where Filter.Entity == T { + self.fetcher = filter?.fetcher + self.filter = filter?.filter self.sorting = sorting self.percy = percy reloadData() @@ -46,7 +57,7 @@ public final class LiveList { } public func reloadData() { - let items: [T] = percy.getEntities(predicate: filter, sortDescriptors: nil, fetchLimit: nil) + let items: [T] = fetcher?(percy) ?? percy.getEntities(predicate: nil, sortDescriptors: nil, fetchLimit: nil) self.items = sorting.flatMap { items.sorted(by: $0) } ?? items } @@ -105,7 +116,7 @@ public final class LiveList { private func handleUpdate(_ entity: T, object: NSManagedObject, changeHandler: (Change) -> Void) { if let filter = self.filter { let currentObjectIndex = items.firstIndex { $0.id == entity.id } - let isNewObjectConformsFilter = filter.evaluate(with: object) + let isNewObjectConformsFilter = filter(entity, object) switch (currentObjectIndex, isNewObjectConformsFilter) { case (let index?, true): handleUpdateAtIndex(entity, index: index, changeHandler: changeHandler) @@ -143,7 +154,7 @@ public final class LiveList { private func handleInsert(_ entity: T, object: NSManagedObject, changeHandler: (Change) -> Void){ if let filter = self.filter { - if filter.evaluate(with: object) { + if filter(entity, object) { self.insertEntity(entity, changeHandler: changeHandler) } } @@ -169,3 +180,34 @@ public final class LiveList { } } + +// MARK: - Type erasure + +fileprivate extension LiveListFilter { + var fetcher: (Percy) -> [Entity] { + return { self.getFilteredEntities(from: $0) } + } + var filter: (Entity, NSManagedObject) -> Bool { + return { self.evaluate(with: $0, sourceObject: $1) } + } +} + +// MARK: - Default predicate filter + +fileprivate final class PredicateFilter: LiveListFilter { + + let predicate: NSPredicate + + init(predicate: NSPredicate) { + self.predicate = predicate + } + + func getFilteredEntities(from percy: Percy) -> [T] { + return percy.getEntities(predicate: predicate, sortDescriptors: nil, fetchLimit: nil) + } + + func evaluate(with entity: T, sourceObject object: NSManagedObject) -> Bool { + return predicate.evaluate(with: object) + } + +}