I finally succeed doing what i wanted!
Not perfect at all, feel free to comment for architecture improvements (especially on the asBaseProtocol() part...)
Here's my complete code (Swift 3.0)
DataFilter
protocol DataFilterDelegate : class {
func didFilter()
func didUpdateItems()
}
class DataFilter<T> {
public weak var delegate : DataFilterDelegate?
private var items : [SelectableItem<T>]?
private var filteredItems : [SelectableItem<T>]?
var source: [SelectableItem<T>]? {
get {
if filteredItems != nil {
return filteredItems
}
return items
}
}
var filter : (T,String) -> Bool
var populateCell : (T) -> UITableViewCell
init(filter : @escaping (T,String) -> Bool, populateCell: @escaping (T) -> UITableViewCell) {
self.filter = filter
self.populateCell = populateCell
}
func updateItems(_ items: [T]) {
self.items = [SelectableItem<T>]()
for item in items {
self.items?.append(SelectableItem(item))
}
delegate?.didUpdateItems()
}
func filterItems(text : String) {
filteredItems = (text == "") ? nil : items?.filter { item in
filter(item.item, text)
}
delegate?.didFilter()
}
func selectedItems() -> [T]? {
guard let items = items else {
return nil
}
var selectedItems = [T]()
for item in items {
if item.isSelected {
selectedItems.append(item.item)
}
}
return selectedItems
}
}
extension DataFilter where T : FIRDataObject {
func asBaseProtocol() -> DataFilter<FIRDataObject> {
return DataFilter<FIRDataObject>(filter: filterAsBaseProtocol(), populateCell: populateCellAsBaseProtocol())
}
private func filterAsBaseProtocol() -> ((FIRDataObject,String) -> Bool) {
return { (object, text) -> Bool in
self.filter(object as! T, text)
}
}
private func populateCellAsBaseProtocol() -> ((FIRDataObject) -> UITableViewCell) {
return { (object) -> UITableViewCell in
self.populateCell(object as! T)
}
}
}
ParentViewController Class
class ParentViewController : UIViewController {
public var dataFilter : DataFilter<FIRDataObject>? {
didSet {
dataFilter!.delegate = self
}
}
// Some Functions using dataFilter
}
ChildViewController Class
class ChildViewController : Parent {
// Stored as a variable to not have to cast objects to the good type everytime I access dataFilter
private var patientDataFilter = DataFilter<Patient>(filter: { patient, text in
patient.firstname.contains(text) ||
patient.lastname.contains(text)
}
, populateCell: { patient in
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "Patient")
cell.textLabel?.text = patient.lastname + " " + patient.firstname
cell.detailTextLabel?.text = patient.postalCode + " " + patient.city
return cell
})
override func viewDidLoad() {
super.viewDidLoad()
dataFilter = patientDataFilter.asBaseProtocol()
}
func someFunc() {
let patient1 = patientDataFilter.source[0].item
// OR
let patient2 = dataFilter.source[0].item as! Patient
}
}